summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/Notification.java5
-rw-r--r--core/java/android/app/NotificationManager.java16
-rw-r--r--core/java/android/app/UiAutomation.java44
-rw-r--r--core/java/android/app/notification.aconfig7
-rw-r--r--core/java/android/app/supervision/ISupervisionManager.aidl1
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java17
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/content/Context.java4
-rw-r--r--core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java38
-rw-r--r--core/java/android/os/BatteryUsageStats.java11
-rw-r--r--core/java/android/os/BatteryUsageStatsQuery.java25
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java50
-rw-r--r--core/java/android/os/ConcurrentMessageQueue/MessageQueue.java14
-rw-r--r--core/java/android/os/LegacyMessageQueue/MessageQueue.java36
-rw-r--r--core/java/android/view/IWindowManager.aidl8
-rw-r--r--core/java/android/view/InsetsController.java23
-rw-r--r--core/java/android/view/ViewRootImpl.java53
-rw-r--r--core/java/android/view/ViewRootInsetsControllerHost.java7
-rw-r--r--core/java/android/window/DesktopModeFlags.java22
-rw-r--r--core/java/android/window/WindowContainerTransaction.java725
-rw-r--r--core/java/android/window/flags/device_state_auto_rotate_setting.aconfig22
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig10
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig7
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java51
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java29
-rw-r--r--core/java/com/android/internal/widget/MessagingData.java13
-rw-r--r--core/java/com/android/internal/widget/MessagingMessage.java2
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressDrawable.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java116
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java31
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java219
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java244
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java232
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java185
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java168
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java36
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java72
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java31
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java25
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java51
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java109
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java31
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java53
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java53
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java199
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java25
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java122
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java6
-rw-r--r--core/tests/coretests/src/android/app/NotificationManagerTest.java44
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt4
-rw-r--r--packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java6
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java47
-rw-r--r--packages/SystemUI/AndroidManifest.xml11
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt79
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt98
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt78
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt39
-rw-r--r--packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml2
-rw-r--r--packages/SystemUI/res/layout/low_light_clock_dream.xml39
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item_advanced.xml1
-rw-r--r--packages/SystemUI/res/layout/shade_carrier.xml2
-rw-r--r--packages/SystemUI/res/layout/shade_carrier_group.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/res/values/styles.xml8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl)4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt168
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt137
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java258
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java127
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt (renamed from packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt)14
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiState.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java (renamed from packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java)126
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt20
-rw-r--r--packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java161
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java226
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt75
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt)54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt92
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt1
-rw-r--r--packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/VcnManagementService.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java3
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java73
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java76
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java45
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java16
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginManager.java10
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginStorage.java200
-rw-r--r--services/core/java/com/android/server/infra/OWNERS1
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java20
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionStopController.java32
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java16
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java29
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java144
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java5
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java5
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java21
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java34
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java150
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java12
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionUserData.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt75
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java43
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt50
-rw-r--r--services/tests/uiservicestests/Android.bp1
-rw-r--r--services/tests/uiservicestests/AndroidManifest.xml2
-rw-r--r--services/tests/uiservicestests/src/android/app/ExampleActivity.java20
-rw-r--r--services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java284
-rw-r--r--services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java71
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java39
-rw-r--r--telephony/java/com/android/internal/telephony/util/WorkerThread.java130
-rw-r--r--tests/testables/src/android/testing/OWNERS2
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java58
-rw-r--r--tests/utils/testutils/java/android/os/test/OWNERS3
-rw-r--r--tests/utils/testutils/java/android/os/test/TestLooper.java140
328 files changed, 9417 insertions, 2540 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 3883a9667355..e9a63f74d59f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11134,6 +11134,7 @@ package android.content {
field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TELEPHONY_SERVICE = "phone";
field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
+ field public static final String TETHERING_SERVICE = "tethering";
field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String TV_AD_SERVICE = "tv_ad";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8a5276c1ce08..7483316e94b0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3800,7 +3800,6 @@ package android.content {
field public static final String STATS_MANAGER = "stats";
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
- field public static final String TETHERING_SERVICE = "tethering";
field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e2fe5062d356..36ef4f5f06ee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -478,8 +478,8 @@ package android.app {
method public void destroy();
method @NonNull public java.util.Set<java.lang.String> getAdoptedShellPermissions();
method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
- method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
- method public void injectInputEventToInputFilter(@NonNull android.view.InputEvent);
+ method @Deprecated @FlaggedApi("com.android.input.flags.deprecate_uiautomation_input_injection") public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
+ method @Deprecated @FlaggedApi("com.android.input.flags.deprecate_uiautomation_input_injection") public void injectInputEventToInputFilter(@NonNull android.view.InputEvent);
method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public void removeOverridePermissionState(int, @NonNull String);
method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8fa2362139a1..ff39329a0d2d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -114,7 +114,6 @@ interface INotificationManager
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
- ParceledListSlice getOrCreateNotificationChannels(String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded);
ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getDeletedChannelCount(String pkg, int uid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 35308ee43dea..40db6dd1b0ba 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1772,6 +1772,11 @@ public class Notification implements Parcelable
*/
public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SUMMARIZED_CONTENT = "android.summarization";
+
@UnsupportedAppUsage
private Icon mSmallIcon;
@UnsupportedAppUsage
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1e1ec602d0a2..21dad28560df 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1264,8 +1264,7 @@ public class NotificationManager {
mNotificationChannelListCache.query(new NotificationChannelQuery(
mContext.getOpPackageName(),
mContext.getPackageName(),
- mContext.getUserId(),
- true))); // create (default channel) if needed
+ mContext.getUserId())));
} else {
INotificationManager service = service();
try {
@@ -1293,8 +1292,7 @@ public class NotificationManager {
mNotificationChannelListCache.query(new NotificationChannelQuery(
mContext.getOpPackageName(),
mContext.getPackageName(),
- mContext.getUserId(),
- true))); // create (default channel) if needed
+ mContext.getUserId())));
} else {
INotificationManager service = service();
try {
@@ -1320,8 +1318,7 @@ public class NotificationManager {
return mNotificationChannelListCache.query(new NotificationChannelQuery(
mContext.getOpPackageName(),
mContext.getPackageName(),
- mContext.getUserId(),
- false));
+ mContext.getUserId()));
} else {
INotificationManager service = service();
try {
@@ -1461,8 +1458,8 @@ public class NotificationManager {
public List<NotificationChannel> apply(NotificationChannelQuery query) {
INotificationManager service = service();
try {
- return service.getOrCreateNotificationChannels(query.callingPkg,
- query.targetPkg, query.userId, query.createIfNeeded).getList();
+ return service.getNotificationChannels(query.callingPkg,
+ query.targetPkg, query.userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1490,8 +1487,7 @@ public class NotificationManager {
private record NotificationChannelQuery(
String callingPkg,
String targetPkg,
- int userId,
- boolean createIfNeeded) {}
+ int userId) {}
/**
* @hide
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 063055eb4917..8021ab4865af 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -18,6 +18,8 @@ package android.app;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.input.flags.Flags.FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION;
+
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityService.Callbacks;
@@ -26,6 +28,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -108,7 +111,10 @@ import java.util.concurrent.TimeoutException;
* client should be using a higher-level library or implement high-level functions.
* For example, performing a tap on the screen requires construction and injecting
* of a touch down and up events which have to be delivered to the system by a
- * call to {@link #injectInputEvent(InputEvent, boolean)}.
+ * call to {@link #injectInputEvent(InputEvent, boolean)}. <strong>Note:</strong> For CTS tests, it
+ * is preferable to inject input events using uinput (com.android.cts.input.UinputDevice) or hid
+ * devices (com.android.cts.input.HidDevice). Alternatively, use InjectInputInProcess
+ * (com.android.cts.input.InjectInputInProcess) for in-process injection.
* </p>
* <p>
* The APIs exposed by this class operate across applications enabling a client
@@ -957,9 +963,17 @@ public final class UiAutomation {
* <strong>Note:</strong> It is caller's responsibility to recycle the event.
* </p>
*
- * @param event The event to inject.
- * @param sync Whether to inject the event synchronously.
- * @return Whether event injection succeeded.
+ * <p>
+ * <strong>Note:</strong> Avoid this method when injecting input events in CTS tests. Instead
+ * use uinput (com.android.cts.input.UinputDevice)
+ * or hid devices (com.android.cts.input.HidDevice), as they provide a more accurate simulation
+ * of real device behavior. Alternatively, InjectInputInProcess
+ * (com.android.cts.input.InjectInputProcess) can be used for in-process injection.
+ * </p>
+ *
+ * @param event the event to inject
+ * @param sync whether to inject the event synchronously
+ * @return {@code true} if event injection succeeded
*/
public boolean injectInputEvent(InputEvent event, boolean sync) {
return injectInputEvent(event, sync, true /* waitForAnimations */);
@@ -972,15 +986,21 @@ public final class UiAutomation {
* <strong>Note:</strong> It is caller's responsibility to recycle the event.
* </p>
*
- * @param event The event to inject.
- * @param sync Whether to inject the event synchronously.
- * @param waitForAnimations Whether to wait for all window container animations and surface
- * operations to complete.
- * @return Whether event injection succeeded.
+ * @param event the event to inject
+ * @param sync whether to inject the event synchronously.
+ * @param waitForAnimations whether to wait for all window container animations and surface
+ * operations to complete
+ * @return {@code true} if event injection succeeded
*
+ * @deprecated for CTS tests prefer inject input events using uinput
+ * (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
+ * Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
+ * for in-process injection.
* @hide
*/
@TestApi
+ @Deprecated // Deprecated for CTS tests
+ @FlaggedApi(FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION)
public boolean injectInputEvent(@NonNull InputEvent event, boolean sync,
boolean waitForAnimations) {
try {
@@ -1003,9 +1023,15 @@ public final class UiAutomation {
* Events injected to the input subsystem using the standard {@link #injectInputEvent} method
* skip the accessibility input filter to avoid feedback loops.
*
+ * @deprecated for CTS tests prefer inject input events using uinput
+ * (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
+ * Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
+ * for in-process injection.
* @hide
*/
@TestApi
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_UIAUTOMATION_INPUT_INJECTION)
public void injectInputEventToInputFilter(@NonNull InputEvent event) {
try {
mUiAutomationConnection.injectInputEventToInputFilter(event);
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index a4a6a55fc9ab..733a348aa825 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -62,13 +62,6 @@ flag {
}
flag {
- name: "modes_ui_test"
- namespace: "systemui"
- description: "Guards new CTS tests for Modes; dependent on flags modes_api and modes_ui"
- bug: "360862012"
-}
-
-flag {
name: "modes_hsum"
namespace: "systemui"
description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users"
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 4598421eb3bc..c3f3b1ced33c 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -22,4 +22,5 @@ package android.app.supervision;
*/
interface ISupervisionManager {
boolean isSupervisionEnabledForUser(int userId);
+ String getActiveSupervisionAppPackage(int userId);
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 92241f3634e8..12432ddd0eb9 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,6 +16,7 @@
package android.app.supervision;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
@@ -98,4 +99,20 @@ public class SupervisionManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the package name of the app that is acting as the active supervision app or null if
+ * supervision is disabled.
+ *
+ * @hide
+ */
+ @UserHandleAware
+ @Nullable
+ public String getActiveSupervisionAppPackage() {
+ try {
+ return mService.getActiveSupervisionAppPackage(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 6da2a073ec19..1cf42820f356 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -18,13 +18,6 @@ flag {
}
flag {
- namespace: "virtual_devices"
- name: "media_projection_keyguard_restrictions"
- description: "Auto-stop MP when the device locks"
- bug: "348335290"
-}
-
-flag {
namespace: "virtual_devices"
name: "virtual_display_insets"
description: "APIs for specifying virtual display insets (via cutout)"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8d54673df74c..d811c0791c6c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4964,10 +4964,10 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager}
* for managing tethering functions.
- * @hide
+ *
* @see android.net.TetheringManager
*/
- @SystemApi
+ @SuppressLint("UnflaggedApi")
public static final String TETHERING_SERVICE = "tethering";
/**
diff --git a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
index de93234445ca..d3fb93588762 100644
--- a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
+++ b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.supervision.SupervisionManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
@@ -55,27 +56,44 @@ public class ParentalControlsUtilsInternal {
return null;
}
- public static boolean parentConsentRequired(@NonNull Context context,
- @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality,
+ /** @return true if parental consent is required in order for biometric sensors to be used. */
+ public static boolean parentConsentRequired(
+ @NonNull Context context,
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
@NonNull UserHandle userHandle) {
if (getTestComponentName(context, userHandle.getIdentifier()) != null) {
return true;
}
- return parentConsentRequired(dpm, modality, userHandle);
+ return parentConsentRequired(dpm, sm, modality, userHandle);
}
/**
* @return true if parental consent is required in order for biometric sensors to be used.
*/
- public static boolean parentConsentRequired(@NonNull DevicePolicyManager dpm,
- @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) {
- final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
- if (cn == null) {
- return false;
+ public static boolean parentConsentRequired(
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
+ @NonNull UserHandle userHandle) {
+ final int keyguardDisabledFeatures;
+
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ if (sm != null && !sm.isSupervisionEnabledForUser(userHandle.getIdentifier())) {
+ return false;
+ }
+ // Check for keyguard features disabled by any admin.
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(/* admin= */ null);
+ } else {
+ final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
+ if (cn == null) {
+ return false;
+ }
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
}
- final int keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
final boolean dpmFpDisabled = containsFlag(keyguardDisabledFeatures,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
final boolean dpmFaceDisabled = containsFlag(keyguardDisabledFeatures,
@@ -97,7 +115,9 @@ public class ParentalControlsUtilsInternal {
return consentRequired;
}
+ /** @deprecated Use {@link SupervisionManager} to check for supervision. */
@Nullable
+ @Deprecated
public static ComponentName getSupervisionComponentName(@NonNull DevicePolicyManager dpm,
@NonNull UserHandle userHandle) {
return dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle);
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f913fcfd56d4..86b8fad16275 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -161,6 +161,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
private final List<UserBatteryConsumer> mUserBatteryConsumers;
private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
private final BatteryStatsHistory mBatteryStatsHistory;
+ private final long mPreferredHistoryDurationMs;
private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
private CursorWindow mBatteryConsumersCursorWindow;
@@ -174,6 +175,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
mDischargeDurationMs = builder.mDischargeDurationMs;
mBatteryStatsHistory = builder.mBatteryStatsHistory;
+ mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs;
mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
@@ -402,8 +404,10 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
if (source.readBoolean()) {
mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
+ mPreferredHistoryDurationMs = source.readLong();
} else {
mBatteryStatsHistory = null;
+ mPreferredHistoryDurationMs = 0;
}
}
@@ -428,7 +432,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
if (mBatteryStatsHistory != null) {
dest.writeBoolean(true);
- mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest);
+ mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest, mPreferredHistoryDurationMs);
} else {
dest.writeBoolean(false);
}
@@ -919,6 +923,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
new SparseArray<>();
private BatteryStatsHistory mBatteryStatsHistory;
+ private long mPreferredHistoryDurationMs;
public Builder(@NonNull String[] customPowerComponentNames) {
this(customPowerComponentNames, false, false, false, 0);
@@ -1092,8 +1097,10 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
* Sets the parceled recent history.
*/
@NonNull
- public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) {
+ public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory,
+ long preferredHistoryDurationMs) {
mBatteryStatsHistory = batteryStatsHistory;
+ mPreferredHistoryDurationMs = preferredHistoryDurationMs;
return this;
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 6e67578fadc8..5aed39bd8fa6 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -25,6 +25,7 @@ import com.android.internal.os.MonotonicClock;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
/**
* Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call.
@@ -77,6 +78,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
public static final int FLAG_BATTERY_USAGE_STATS_ACCUMULATED = 0x0080;
private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;
+ private static final long DEFAULT_PREFERRED_HISTORY_DURATION_MS = TimeUnit.HOURS.toMillis(2);
private final int mFlags;
@NonNull
@@ -89,6 +91,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
private long mMonotonicEndTime;
private final double mMinConsumedPowerThreshold;
private final @BatteryConsumer.PowerComponentId int[] mPowerComponents;
+ private final long mPreferredHistoryDurationMs;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
@@ -101,6 +104,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
mMonotonicStartTime = builder.mMonotonicStartTime;
mMonotonicEndTime = builder.mMonotonicEndTime;
mPowerComponents = builder.mPowerComponents;
+ mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs;
}
@BatteryUsageStatsFlags
@@ -197,6 +201,13 @@ public final class BatteryUsageStatsQuery implements Parcelable {
return mAggregatedToTimestamp;
}
+ /**
+ * Returns the preferred duration of battery history (tail) to be included in the query result.
+ */
+ public long getPreferredHistoryDurationMs() {
+ return mPreferredHistoryDurationMs;
+ }
+
@Override
public String toString() {
return "BatteryUsageStatsQuery{"
@@ -209,6 +220,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
+ ", mMonotonicEndTime=" + mMonotonicEndTime
+ ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold
+ ", mPowerComponents=" + Arrays.toString(mPowerComponents)
+ + ", mMaxHistoryDurationMs=" + mPreferredHistoryDurationMs
+ '}';
}
@@ -223,6 +235,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
mAggregatedFromTimestamp = in.readLong();
mAggregatedToTimestamp = in.readLong();
mPowerComponents = in.createIntArray();
+ mPreferredHistoryDurationMs = in.readLong();
}
@Override
@@ -237,6 +250,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
dest.writeLong(mAggregatedFromTimestamp);
dest.writeLong(mAggregatedToTimestamp);
dest.writeIntArray(mPowerComponents);
+ dest.writeLong(mPreferredHistoryDurationMs);
}
@Override
@@ -271,6 +285,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
private long mAggregateToTimestamp;
private double mMinConsumedPowerThreshold = 0;
private @BatteryConsumer.PowerComponentId int[] mPowerComponents;
+ private long mPreferredHistoryDurationMs = DEFAULT_PREFERRED_HISTORY_DURATION_MS;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -311,6 +326,16 @@ public final class BatteryUsageStatsQuery implements Parcelable {
}
/**
+ * Set the preferred amount of battery history to be included in the result, provided
+ * that `includeBatteryHistory` is also called. The actual amount of history included in
+ * the result may vary for performance reasons and may exceed the specified preference.
+ */
+ public Builder setPreferredHistoryDurationMs(long preferredHistoryDurationMs) {
+ mPreferredHistoryDurationMs = preferredHistoryDurationMs;
+ return this;
+ }
+
+ /**
* Requests that per-process state data be included in the BatteryUsageStats, if
* available. Check {@link BatteryUsageStats#isProcessStateDataIncluded()} on the result
* to see if the data is available.
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 1e663342522b..877f130a8b5a 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -601,7 +601,7 @@ public final class MessageQueue {
/* This is only read/written from the Looper thread. For use with Concurrent MQ */
private int mNextPollTimeoutMillis;
private boolean mMessageDirectlyQueued;
- private Message nextMessage(boolean peek) {
+ private Message nextMessage(boolean peek, boolean returnEarliest) {
int i = 0;
while (true) {
@@ -678,7 +678,7 @@ public final class MessageQueue {
* If we have a barrier we should return the async node (if it exists and is ready)
*/
if (msgNode != null && msgNode.isBarrier()) {
- if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) {
+ if (asyncMsgNode != null && (returnEarliest || now >= asyncMsgNode.getWhen())) {
found = asyncMsgNode;
} else {
next = asyncMsgNode;
@@ -692,7 +692,7 @@ public final class MessageQueue {
earliest = pickEarliestNode(msgNode, asyncMsgNode);
if (earliest != null) {
- if (now >= earliest.getWhen()) {
+ if (returnEarliest || now >= earliest.getWhen()) {
found = earliest;
} else {
next = earliest;
@@ -797,7 +797,7 @@ public final class MessageQueue {
mMessageDirectlyQueued = false;
nativePollOnce(ptr, mNextPollTimeoutMillis);
- Message msg = nextMessage(false);
+ Message msg = nextMessage(false, false);
if (msg != null) {
msg.markInUse();
return msg;
@@ -1374,27 +1374,27 @@ public final class MessageQueue {
if (now >= msg.when) {
// Got a message.
mBlocked = false;
- if (prevMsg != null) {
- prevMsg.next = msg.next;
- if (prevMsg.next == null) {
- mLast = prevMsg;
- }
- } else {
- mMessages = msg.next;
- if (msg.next == null) {
- mLast = null;
- }
- }
- msg.next = null;
- msg.markInUse();
- if (msg.isAsynchronous()) {
- mAsyncMessageCount--;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
}
- if (TRACE) {
- Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
}
- return msg;
}
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
}
}
return null;
@@ -1411,7 +1411,7 @@ public final class MessageQueue {
throwIfNotTest();
Message ret;
if (mUseConcurrent) {
- ret = nextMessage(true);
+ ret = nextMessage(true, true);
} else {
ret = legacyPeekOrPoll(true);
}
@@ -1429,7 +1429,7 @@ public final class MessageQueue {
Message pollForTest() {
throwIfNotTest();
if (mUseConcurrent) {
- return nextMessage(false);
+ return nextMessage(false, true);
} else {
return legacyPeekOrPoll(false);
}
@@ -1446,7 +1446,7 @@ public final class MessageQueue {
throwIfNotTest();
if (mUseConcurrent) {
// Call nextMessage to get the stack drained into our priority queues
- nextMessage(true);
+ nextMessage(true, false);
Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
MessageNode queueNode = iterateNext(queueIter);
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 80da487a1358..7e0995c251b8 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -588,7 +588,7 @@ public final class MessageQueue {
private static final AtomicLong mMessagesDelivered = new AtomicLong();
private boolean mMessageDirectlyQueued;
- private Message nextMessage(boolean peek) {
+ private Message nextMessage(boolean peek, boolean returnEarliest) {
int i = 0;
while (true) {
@@ -665,7 +665,7 @@ public final class MessageQueue {
* If we have a barrier we should return the async node (if it exists and is ready)
*/
if (msgNode != null && msgNode.isBarrier()) {
- if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) {
+ if (asyncMsgNode != null && (returnEarliest || now >= asyncMsgNode.getWhen())) {
found = asyncMsgNode;
} else {
next = asyncMsgNode;
@@ -679,7 +679,7 @@ public final class MessageQueue {
earliest = pickEarliestNode(msgNode, asyncMsgNode);
if (earliest != null) {
- if (now >= earliest.getWhen()) {
+ if (returnEarliest || now >= earliest.getWhen()) {
found = earliest;
} else {
next = earliest;
@@ -784,7 +784,7 @@ public final class MessageQueue {
mMessageDirectlyQueued = false;
nativePollOnce(ptr, mNextPollTimeoutMillis);
- Message msg = nextMessage(false);
+ Message msg = nextMessage(false, false);
if (msg != null) {
msg.markInUse();
return msg;
@@ -1089,7 +1089,7 @@ public final class MessageQueue {
*/
Long peekWhenForTest() {
throwIfNotTest();
- Message ret = nextMessage(true);
+ Message ret = nextMessage(true, true);
return ret != null ? ret.when : null;
}
@@ -1102,7 +1102,7 @@ public final class MessageQueue {
@Nullable
Message pollForTest() {
throwIfNotTest();
- return nextMessage(false);
+ return nextMessage(false, true);
}
/**
@@ -1116,7 +1116,7 @@ public final class MessageQueue {
throwIfNotTest();
// Call nextMessage to get the stack drained into our priority queues
- nextMessage(true);
+ nextMessage(true, false);
Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
MessageNode queueNode = iterateNext(queueIter);
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index cde2ba56fcba..132bdd1e56b8 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -759,27 +759,27 @@ public final class MessageQueue {
if (now >= msg.when) {
// Got a message.
mBlocked = false;
- if (prevMsg != null) {
- prevMsg.next = msg.next;
- if (prevMsg.next == null) {
- mLast = prevMsg;
- }
- } else {
- mMessages = msg.next;
- if (msg.next == null) {
- mLast = null;
- }
- }
- msg.next = null;
- msg.markInUse();
- if (msg.isAsynchronous()) {
- mAsyncMessageCount--;
+ }
+ if (prevMsg != null) {
+ prevMsg.next = msg.next;
+ if (prevMsg.next == null) {
+ mLast = prevMsg;
}
- if (TRACE) {
- Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ } else {
+ mMessages = msg.next;
+ if (msg.next == null) {
+ mLast = null;
}
- return msg;
}
+ msg.next = null;
+ msg.markInUse();
+ if (msg.isAsynchronous()) {
+ mAsyncMessageCount--;
+ }
+ if (TRACE) {
+ Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+ }
+ return msg;
}
}
return null;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2ec5dbc5612a..f58baffb1367 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -1146,14 +1146,6 @@ interface IWindowManager
*/
KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId);
- /*
- * Notifies about IME insets animation.
- *
- * @param running Indicates the insets animation state.
- * @param animationType Indicates the {@link InsetsController.AnimationType}
- */
- oneway void notifyImeInsetsAnimationStateChanged(boolean running, int animationType);
-
/**
* Returns whether the display with {@code displayId} ignores orientation request.
*/
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e097a0764512..c174fbe0bbcd 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -214,14 +214,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
* Notifies when the state of running animation is changed. The state is either "running" or
* "idle".
*
- * @param running {@code true} if the given insets types start running
- * {@code false} otherwise.
- * @param animationType {@link AnimationType}
- * @param insetsTypes {@link Type}.
+ * @param running {@code true} if there is any animation running; {@code false} otherwise.
*/
- default void notifyAnimationRunningStateChanged(boolean running,
- @AnimationType int animationType, @InsetsType int insetsTypes) {
- }
+ default void notifyAnimationRunningStateChanged(boolean running) {}
/** @see ViewRootImpl#isHandlingPointerEvent */
default boolean isHandlingPointerEvent() {
@@ -749,8 +744,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
mFrame, mFromState, mToState, RESIZE_INTERPOLATOR,
ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
- mHost.notifyAnimationRunningStateChanged(true,
- runner.getAnimationType(), mTypes);
+ if (mRunningAnimations.isEmpty()) {
+ mHost.notifyAnimationRunningStateChanged(true);
+ }
mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
}
};
@@ -1564,7 +1560,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
- mHost.notifyAnimationRunningStateChanged(true, animationType, types);
+ if (mRunningAnimations.isEmpty()) {
+ mHost.notifyAnimationRunningStateChanged(true);
+ }
mRunningAnimations.add(new RunningAnimation(runner, animationType));
if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
+ useInsetsAnimationThread);
@@ -1844,8 +1842,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
break;
}
}
- mHost.notifyAnimationRunningStateChanged(
- false, control.getAnimationType(), removedTypes);
+ if (mRunningAnimations.isEmpty()) {
+ mHost.notifyAnimationRunningStateChanged(false);
+ }
onAnimationStateChanged(removedTypes, false /* running */);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7c5b300e9d24..cd8a85a66c1a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,7 +24,6 @@ import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
import static android.os.Trace.TRACE_TAG_VIEW;
-import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.util.SequenceUtils.getInitSeq;
import static android.util.SequenceUtils.isIncomingSeqStale;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -923,8 +922,6 @@ public final class ViewRootImpl implements ViewParent,
private boolean mInsetsAnimationRunning;
- private int mInsetsAnimatingTypes = 0;
-
private long mPreviousFrameDrawnTime = -1;
// The largest view size percentage to the display size. Used on trace to collect metric.
private float mLargestChildPercentage = 0.0f;
@@ -2523,49 +2520,17 @@ public final class ViewRootImpl implements ViewParent,
* Notify the when the running state of a insets animation changed.
*/
@VisibleForTesting
- public void notifyInsetsAnimationRunningStateChanged(boolean running,
- @InsetsController.AnimationType int animationType,
- @InsetsType int insetsTypes) {
- @InsetsType int previousInsetsType = mInsetsAnimatingTypes;
- // If improveFillDialogAconfig is disabled, we notify WindowSession of all the updates we
- // receive here
- boolean notifyWindowSession = !improveFillDialogAconfig();
- if (running) {
- mInsetsAnimatingTypes |= insetsTypes;
- } else {
- mInsetsAnimatingTypes &= ~insetsTypes;
- }
+ public void notifyInsetsAnimationRunningStateChanged(boolean running) {
if (sToolkitSetFrameRateReadOnlyFlagValue) {
- mInsetsAnimationRunning = running;
- // If improveFillDialogAconfig is enabled, we need to confirm other animations aren't
- // running to maintain the existing behavior. System server were notified previously
- // only when animation started running or stopped when there were no running animations.
- if (improveFillDialogAconfig()) {
- if ((previousInsetsType == 0 && mInsetsAnimatingTypes != 0)
- || (previousInsetsType != 0 && mInsetsAnimatingTypes == 0)) {
- notifyWindowSession = true;
- }
- }
- if (notifyWindowSession) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.instant(Trace.TRACE_TAG_VIEW,
- TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
- Boolean.toString(running)));
- }
- try {
- mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
- } catch (RemoteException e) {
- }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW,
+ TextUtils.formatSimple("notifyInsetsAnimationRunningStateChanged(%s)",
+ Boolean.toString(running)));
}
- }
- if (improveFillDialogAconfig()) {
- // Update WindowManager for ImeAnimation
- if ((insetsTypes & WindowInsets.Type.ime()) != 0) {
- try {
- WindowManagerGlobal.getWindowManagerService()
- .notifyImeInsetsAnimationStateChanged(running, animationType);
- } catch (RemoteException e) {
- }
+ mInsetsAnimationRunning = running;
+ try {
+ mWindowSession.notifyInsetsAnimationRunningStateChanged(mWindow, running);
+ } catch (RemoteException e) {
}
}
}
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index f1666dbebd7b..889acca4b8b1 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -275,12 +275,9 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
}
@Override
- public void notifyAnimationRunningStateChanged(boolean running,
- @InsetsController.AnimationType int animationType,
- @WindowInsets.Type.InsetsType int insetsTypes) {
+ public void notifyAnimationRunningStateChanged(boolean running) {
if (mViewRoot != null) {
- mViewRoot.notifyInsetsAnimationRunningStateChanged(
- running, animationType, insetsTypes);
+ mViewRoot.notifyInsetsAnimationRunningStateChanged(running);
}
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 82055afda8c1..d1e3a2d953ef 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -35,10 +35,6 @@ 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.
*
@@ -85,18 +81,24 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX(
- Flags::enableDesktopWindowingEnterTransitionBugfix, false),
+ Flags::enableDesktopWindowingEnterTransitionBugfix, true),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX(
- Flags::enableDesktopWindowingExitTransitionsBugfix, false),
+ Flags::enableDesktopWindowingExitTransitionsBugfix, true),
ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
- Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, false),
+ Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, true),
ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
- Flags::enableDesktopAppLaunchTransitionsBugfix, false),
+ Flags::enableDesktopAppLaunchTransitionsBugfix, true),
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
+ ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
- ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true);
+ ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
+ ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER(
+ Flags::enableDesktopWallpaperActivityForSystemUser, true),
+ ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
+ Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
+ ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true);
/**
* Flag class, to be used in case the enum cannot be used because the flag is not accessible.
@@ -116,7 +118,7 @@ public enum DesktopModeFlags {
/**
* Determines state of flag based on the actual flag and desktop mode developer option
- * or desktop experience developer option overrides.
+ * overrides.
*/
public boolean isTrue() {
return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ce0ccd5c6d0d..68b5a261f507 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -112,6 +112,12 @@ public final class WindowContainerTransaction implements Parcelable {
mTaskFragmentOrganizer = null;
}
+ /*
+ * ===========================================================================================
+ * Window container properties
+ * ===========================================================================================
+ */
+
/**
* Resize a container.
*/
@@ -170,20 +176,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
- * has finished the enter animation with the given bounds.
- */
- @NonNull
- public WindowContainerTransaction scheduleFinishEnterPip(
- @NonNull WindowContainerToken container, @NonNull Rect bounds) {
- final Change chg = getOrCreateChange(container.asBinder());
- chg.mPinnedBounds = new Rect(bounds);
- chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
-
- return this;
- }
-
- /**
* Send a SurfaceControl transaction to the server, which the server will apply in sync with
* the next bounds change. As this uses deferred transaction and not BLAST it is only
* able to sync with a single window, and the first visible window in this hierarchy of type
@@ -204,36 +196,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop
- * on a container's surface control. This is useful when a boundsChangeTransaction needs to be
- * queued up on a Task that won't be organized until the end of this window-container
- * transaction.
- *
- * This requires that, at the end of this transaction, `task` will be organized; otherwise
- * the server will throw an IllegalArgumentException.
- *
- * WARNING: Use this carefully. Whatever is set here should match the expected bounds after
- * the transaction completes since it will likely be replaced by it. This call is
- * intended to pre-emptively set bounds on a surface in sync with a buffer when
- * otherwise the new bounds and the new buffer would update on different frames.
- *
- * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled.
- *
- * @hide
- */
- @NonNull
- public WindowContainerTransaction setBoundsChangeTransaction(
- @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) {
- Change chg = getOrCreateChange(task.asBinder());
- if (chg.mBoundsChangeSurfaceBounds == null) {
- chg.mBoundsChangeSurfaceBounds = new Rect();
- }
- chg.mBoundsChangeSurfaceBounds.set(surfaceBounds);
- chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT;
- return this;
- }
-
- /**
* Set the windowing mode of children of a given root task, without changing
* the windowing mode of the Task itself. This can be used during transitions
* for example to make the activity render it's fullscreen configuration
@@ -381,22 +343,115 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Reparents a container into another one. The effect of a {@code null} parent can vary. For
- * example, reparenting a stack to {@code null} will reparent it to its display.
+ * Sets whether a container is being drag-resized.
+ * When {@code true}, the client will reuse a single (larger) surface size to avoid
+ * continuous allocations on every size change.
*
- * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
- * the bottom.
+ * @param container WindowContainerToken of the task that changed its drag resizing state
+ * @hide
*/
@NonNull
- public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
- @Nullable WindowContainerToken parent, boolean onTop) {
- mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
- parent == null ? null : parent.asBinder(),
- onTop));
+ public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
+ boolean dragResizing) {
+ final Change change = getOrCreateChange(container.asBinder());
+ change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
+ change.mDragResizing = dragResizing;
return this;
}
/**
+ * Sets/removes the always on top flag for this {@code windowContainer}. See
+ * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
+ * Please note that this method is only intended to be used for a
+ * {@link com.android.server.wm.Task} or {@link com.android.server.wm.DisplayArea}.
+ *
+ * <p>
+ * Setting always on top to {@code True} will also make the {@code windowContainer} to move
+ * to the top.
+ * </p>
+ * <p>
+ * Setting always on top to {@code False} will make this {@code windowContainer} to move
+ * below the other always on top sibling containers.
+ * </p>
+ *
+ * @param windowContainer the container which the flag need to be updated for.
+ * @param alwaysOnTop denotes whether or not always on top flag should be set.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setAlwaysOnTop(
+ @NonNull WindowContainerToken windowContainer, boolean alwaysOnTop) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
+ .setContainer(windowContainer.asBinder())
+ .setAlwaysOnTop(alwaysOnTop)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Sets/removes the reparent leaf task flag for this {@code windowContainer}.
+ * When this is set, the server side will try to reparent the leaf task to task display area
+ * if there is an existing activity in history during the activity launch. This operation only
+ * support on the organized root task.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setReparentLeafTaskIfRelaunch(
+ @NonNull WindowContainerToken windowContainer, boolean reparentLeafTaskIfRelaunch) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH)
+ .setContainer(windowContainer.asBinder())
+ .setReparentLeafTaskIfRelaunch(reparentLeafTaskIfRelaunch)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Defers client-facing configuration changes for activities in `container` until the end of
+ * the transition animation. The configuration will still be applied to the WMCore hierarchy
+ * at the normal time (beginning); so, special consideration must be made for this in the
+ * animation.
+ *
+ * @param container WindowContainerToken who's children should defer config notification.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction deferConfigToTransitionEnd(
+ @NonNull WindowContainerToken container) {
+ final Change change = getOrCreateChange(container.asBinder());
+ change.mConfigAtTransitionEnd = true;
+ return this;
+ }
+
+ /**
+ * Sets the task as trimmable or not. This can be used to prevent the task from being trimmed by
+ * recents. This attribute is set to true on task creation by default.
+ *
+ * @param isTrimmableFromRecents When {@code true}, task is set as trimmable from recents.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setTaskTrimmableFromRecents(
+ @NonNull WindowContainerToken container,
+ boolean isTrimmableFromRecents) {
+ mHierarchyOps.add(
+ HierarchyOp.createForSetTaskTrimmableFromRecents(container.asBinder(),
+ isTrimmableFromRecents));
+ return this;
+ }
+
+ /*
+ * ===========================================================================================
+ * Hierarchy updates (create/destroy/reorder/reparent containers)
+ * ===========================================================================================
+ */
+
+ /**
* Reorders a container within its parent.
*
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
@@ -425,6 +480,22 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Reparents a container into another one. The effect of a {@code null} parent can vary. For
+ * example, reparenting a stack to {@code null} will reparent it to its display.
+ *
+ * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+ * the bottom.
+ */
+ @NonNull
+ public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
+ @Nullable WindowContainerToken parent, boolean onTop) {
+ mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
+ parent == null ? null : parent.asBinder(),
+ onTop));
+ return this;
+ }
+
+ /**
* Reparent's all children tasks or the top task of {@param currentParent} in the specified
* {@param windowingMode} and {@param activityType} to {@param newParent} in their current
* z-order.
@@ -478,6 +549,116 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Finds and removes a task and its children using its container token. The task is removed
+ * from recents.
+ *
+ * If the task is a root task, its leaves are removed but the root task is not. Use
+ * {@link #removeRootTask(WindowContainerToken)} to remove the root task.
+ *
+ * @param containerToken ContainerToken of Task to be removed
+ */
+ @NonNull
+ public WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {
+ mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));
+ return this;
+ }
+
+ /**
+ * Finds and removes a root task created by an organizer and its leaves using its container
+ * token.
+ *
+ * @param containerToken ContainerToken of the root task to be removed
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction removeRootTask(@NonNull WindowContainerToken containerToken) {
+ mHierarchyOps.add(HierarchyOp.createForRemoveRootTask(containerToken.asBinder()));
+ return this;
+ }
+
+ /**
+ * If `container` was brought to front as a transient-launch (eg. recents), this will reorder
+ * the container back to where it was prior to the transient-launch. This way if a transient
+ * launch is "aborted", the z-ordering of containers in WM should be restored to before the
+ * launch.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction restoreTransientOrder(
+ @NonNull WindowContainerToken container) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER)
+ .setContainer(container.asBinder())
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Restore the back navigation target from visible to invisible for canceling gesture animation.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction restoreBackNavi() {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /*
+ * ===========================================================================================
+ * Activity launch
+ * ===========================================================================================
+ */
+
+ /**
+ * Starts a task by id. The task is expected to already exist (eg. as a recent task).
+ * @param taskId Id of task to start.
+ * @param options bundle containing ActivityOptions for the task's top activity.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+ mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
+ return this;
+ }
+
+ /**
+ * Sends a pending intent in sync.
+ * @param sender The PendingIntent sender.
+ * @param intent The fillIn intent to patch over the sender's base intent.
+ * @param options bundle containing ActivityOptions for the task's top activity.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
+ @Nullable Intent intent, @Nullable Bundle options) {
+ mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
+ .setLaunchOptions(options)
+ .setPendingIntent(sender)
+ .setActivityIntent(intent)
+ .build());
+ return this;
+ }
+
+ /**
+ * Starts activity(s) from a shortcut.
+ * @param callingPackage The package launching the shortcut.
+ * @param shortcutInfo Information about the shortcut to start
+ * @param options bundle containing ActivityOptions for the task's top activity.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+ mHierarchyOps.add(HierarchyOp.createForStartShortcut(
+ callingPackage, shortcutInfo, options));
+ return this;
+ }
+
+ /**
* Sets whether a container should be the launch root for the specified windowing mode and
* activity type. This currently only applies to Task containers created by organizer.
*/
@@ -491,6 +672,12 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ /*
+ * ===========================================================================================
+ * Multitasking
+ * ===========================================================================================
+ */
+
/**
* Sets two containers adjacent to each other. Containers below two visible adjacent roots will
* be made invisible. This currently only applies to TaskFragment containers created by
@@ -599,93 +786,162 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ /*
+ * ===========================================================================================
+ * PIP
+ * ===========================================================================================
+ */
+
/**
- * Starts a task by id. The task is expected to already exist (eg. as a recent task).
- * @param taskId Id of task to start.
- * @param options bundle containing ActivityOptions for the task's top activity.
+ * Moves the PiP activity of a parent task to a pinned root task.
+ * @param parentToken the parent task of the PiP activity
+ * @param bounds the entry bounds
* @hide
*/
@NonNull
- public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
- mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
+ public WindowContainerTransaction movePipActivityToPinnedRootTask(
+ @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) {
+ mHierarchyOps.add(new HierarchyOp
+ .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK)
+ .setContainer(parentToken.asBinder())
+ .setBounds(bounds)
+ .build());
return this;
}
/**
- * Finds and removes a task and its children using its container token. The task is removed
- * from recents.
- *
- * If the task is a root task, its leaves are removed but the root task is not. Use
- * {@link #removeRootTask(WindowContainerToken)} to remove the root task.
- *
- * @param containerToken ContainerToken of Task to be removed
+ * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task
+ * has finished the enter animation with the given bounds.
*/
@NonNull
- public WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {
- mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));
+ public WindowContainerTransaction scheduleFinishEnterPip(
+ @NonNull WindowContainerToken container, @NonNull Rect bounds) {
+ final Change chg = getOrCreateChange(container.asBinder());
+ chg.mPinnedBounds = new Rect(bounds);
+ chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
+
return this;
}
+ /*
+ * ===========================================================================================
+ * Insets
+ * ===========================================================================================
+ */
+
/**
- * Finds and removes a root task created by an organizer and its leaves using its container
- * token.
+ * Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
*
- * @param containerToken ContainerToken of the root task to be removed
+ * @param receiver The window container that the insets source is added to.
+ * @param owner The owner of the insets source. An insets source can only be modified by its
+ * owner.
+ * @param index An owner might add multiple insets sources with the same type.
+ * This identifies them.
+ * @param type The {@link InsetsType} of the insets source.
+ * @param frame The rectangle area of the insets source.
+ * @param boundingRects The bounding rects within this inset, relative to the |frame|.
* @hide
*/
@NonNull
- public WindowContainerTransaction removeRootTask(@NonNull WindowContainerToken containerToken) {
- mHierarchyOps.add(HierarchyOp.createForRemoveRootTask(containerToken.asBinder()));
+ public WindowContainerTransaction addInsetsSource(
+ @NonNull WindowContainerToken receiver,
+ @Nullable IBinder owner, int index, @InsetsType int type, @Nullable Rect frame,
+ @Nullable Rect[] boundingRects, @InsetsSource.Flags int flags) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER)
+ .setContainer(receiver.asBinder())
+ .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)
+ .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE)
+ .setArbitraryRectangle(frame)
+ .setBoundingRects(boundingRects)
+ .setFlags(flags))
+ .setInsetsFrameOwner(owner)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
return this;
}
/**
- * Sets whether a container is being drag-resized.
- * When {@code true}, the client will reuse a single (larger) surface size to avoid
- * continuous allocations on every size change.
+ * Removes the insets source from the {@code receiver}.
*
- * @param container WindowContainerToken of the task that changed its drag resizing state
+ * @param receiver The window container that the insets source was added to.
+ * @param owner The owner of the insets source. An insets source can only be modified by its
+ * owner.
+ * @param index An owner might add multiple insets sources with the same type.
+ * This identifies them.
+ * @param type The {@link InsetsType} of the insets source.
* @hide
*/
@NonNull
- public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
- boolean dragResizing) {
- final Change change = getOrCreateChange(container.asBinder());
- change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
- change.mDragResizing = dragResizing;
+ public WindowContainerTransaction removeInsetsSource(@NonNull WindowContainerToken receiver,
+ @Nullable IBinder owner, int index, @InsetsType int type) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
+ .setContainer(receiver.asBinder())
+ .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type))
+ .setInsetsFrameOwner(owner)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
return this;
}
+ /*
+ * ===========================================================================================
+ * Keyguard
+ * ===========================================================================================
+ */
+
/**
- * Sends a pending intent in sync.
- * @param sender The PendingIntent sender.
- * @param intent The fillIn intent to patch over the sender's base intent.
- * @param options bundle containing ActivityOptions for the task's top activity.
+ * Adds a {@link KeyguardState} to apply to the given displays.
+ *
* @hide
*/
@NonNull
- public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
- @Nullable Intent intent, @Nullable Bundle options) {
- mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
- .setLaunchOptions(options)
- .setPendingIntent(sender)
- .setActivityIntent(intent)
- .build());
+ public WindowContainerTransaction addKeyguardState(@NonNull KeyguardState keyguardState) {
+ Objects.requireNonNull(keyguardState);
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE)
+ .setKeyguardState(keyguardState)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
return this;
}
+ /*
+ * ===========================================================================================
+ * Task fragments
+ * ===========================================================================================
+ */
+
/**
- * Starts activity(s) from a shortcut.
- * @param callingPackage The package launching the shortcut.
- * @param shortcutInfo Information about the shortcut to start
- * @param options bundle containing ActivityOptions for the task's top activity.
+ * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
+ * When this is set, the server side will not check for the permission of
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
+ * contains operations that are allowed for this organizer, such as modifying TaskFragments that
+ * are organized by this organizer.
* @hide
*/
@NonNull
- public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
- @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
- mHierarchyOps.add(HierarchyOp.createForStartShortcut(
- callingPackage, shortcutInfo, options));
+ public WindowContainerTransaction setTaskFragmentOrganizer(
+ @NonNull ITaskFragmentOrganizer organizer) {
+ mTaskFragmentOrganizer = organizer;
+ return this;
+ }
+
+ /**
+ * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+ * trigger callback with this {@param errorCallbackToken}.
+ * @param errorCallbackToken client provided token that will be passed back as parameter in
+ * the callback if there is an error on the server side.
+ * @see ITaskFragmentOrganizer#onTaskFragmentError
+ */
+ @NonNull
+ public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+ if (mErrorCallbackToken != null) {
+ throw new IllegalStateException("Can't set multiple error token for one transaction.");
+ }
+ mErrorCallbackToken = errorCallbackToken;
return this;
}
@@ -793,93 +1049,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * If `container` was brought to front as a transient-launch (eg. recents), this will reorder
- * the container back to where it was prior to the transient-launch. This way if a transient
- * launch is "aborted", the z-ordering of containers in WM should be restored to before the
- * launch.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction restoreTransientOrder(
- @NonNull WindowContainerToken container) {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER)
- .setContainer(container.asBinder())
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * Restore the back navigation target from visible to invisible for canceling gesture animation.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction restoreBackNavi() {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
- *
- * @param receiver The window container that the insets source is added to.
- * @param owner The owner of the insets source. An insets source can only be modified by its
- * owner.
- * @param index An owner might add multiple insets sources with the same type.
- * This identifies them.
- * @param type The {@link InsetsType} of the insets source.
- * @param frame The rectangle area of the insets source.
- * @param boundingRects The bounding rects within this inset, relative to the |frame|.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction addInsetsSource(
- @NonNull WindowContainerToken receiver,
- @Nullable IBinder owner, int index, @InsetsType int type, @Nullable Rect frame,
- @Nullable Rect[] boundingRects, @InsetsSource.Flags int flags) {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER)
- .setContainer(receiver.asBinder())
- .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)
- .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE)
- .setArbitraryRectangle(frame)
- .setBoundingRects(boundingRects)
- .setFlags(flags))
- .setInsetsFrameOwner(owner)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * Removes the insets source from the {@code receiver}.
- *
- * @param receiver The window container that the insets source was added to.
- * @param owner The owner of the insets source. An insets source can only be modified by its
- * owner.
- * @param index An owner might add multiple insets sources with the same type.
- * This identifies them.
- * @param type The {@link InsetsType} of the insets source.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction removeInsetsSource(@NonNull WindowContainerToken receiver,
- @Nullable IBinder owner, int index, @InsetsType int type) {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
- .setContainer(receiver.asBinder())
- .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type))
- .setInsetsFrameOwner(owner)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
* Requests focus on the top running Activity in the given TaskFragment. This will only take
* effect if there is no focus, or if the current focus is in the same Task as the requested
* TaskFragment.
@@ -961,157 +1130,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
- * Adds a {@link KeyguardState} to apply to the given displays.
- *
- * @hide
- */
- @NonNull
- public WindowContainerTransaction addKeyguardState(@NonNull KeyguardState keyguardState) {
- Objects.requireNonNull(keyguardState);
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(
- HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE)
- .setKeyguardState(keyguardState)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * Sets/removes the always on top flag for this {@code windowContainer}. See
- * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
- * Please note that this method is only intended to be used for a
- * {@link com.android.server.wm.Task} or {@link com.android.server.wm.DisplayArea}.
- *
- * <p>
- * Setting always on top to {@code True} will also make the {@code windowContainer} to move
- * to the top.
- * </p>
- * <p>
- * Setting always on top to {@code False} will make this {@code windowContainer} to move
- * below the other always on top sibling containers.
- * </p>
- *
- * @param windowContainer the container which the flag need to be updated for.
- * @param alwaysOnTop denotes whether or not always on top flag should be set.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction setAlwaysOnTop(
- @NonNull WindowContainerToken windowContainer, boolean alwaysOnTop) {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(
- HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
- .setContainer(windowContainer.asBinder())
- .setAlwaysOnTop(alwaysOnTop)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
- * trigger callback with this {@param errorCallbackToken}.
- * @param errorCallbackToken client provided token that will be passed back as parameter in
- * the callback if there is an error on the server side.
- * @see ITaskFragmentOrganizer#onTaskFragmentError
- */
- @NonNull
- public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
- if (mErrorCallbackToken != null) {
- throw new IllegalStateException("Can't set multiple error token for one transaction.");
- }
- mErrorCallbackToken = errorCallbackToken;
- return this;
- }
-
- /**
- * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
- * When this is set, the server side will not check for the permission of
- * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
- * contains operations that are allowed for this organizer, such as modifying TaskFragments that
- * are organized by this organizer.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction setTaskFragmentOrganizer(
- @NonNull ITaskFragmentOrganizer organizer) {
- mTaskFragmentOrganizer = organizer;
- return this;
- }
-
- /**
- * Sets/removes the reparent leaf task flag for this {@code windowContainer}.
- * When this is set, the server side will try to reparent the leaf task to task display area
- * if there is an existing activity in history during the activity launch. This operation only
- * support on the organized root task.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction setReparentLeafTaskIfRelaunch(
- @NonNull WindowContainerToken windowContainer, boolean reparentLeafTaskIfRelaunch) {
- final HierarchyOp hierarchyOp =
- new HierarchyOp.Builder(
- HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH)
- .setContainer(windowContainer.asBinder())
- .setReparentLeafTaskIfRelaunch(reparentLeafTaskIfRelaunch)
- .build();
- mHierarchyOps.add(hierarchyOp);
- return this;
- }
-
- /**
- * Moves the PiP activity of a parent task to a pinned root task.
- * @param parentToken the parent task of the PiP activity
- * @param bounds the entry bounds
- * @hide
- */
- @NonNull
- public WindowContainerTransaction movePipActivityToPinnedRootTask(
- @NonNull WindowContainerToken parentToken, @NonNull Rect bounds) {
- mHierarchyOps.add(new HierarchyOp
- .Builder(HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK)
- .setContainer(parentToken.asBinder())
- .setBounds(bounds)
- .build());
- return this;
- }
-
- /**
- * Defers client-facing configuration changes for activities in `container` until the end of
- * the transition animation. The configuration will still be applied to the WMCore hierarchy
- * at the normal time (beginning); so, special consideration must be made for this in the
- * animation.
- *
- * @param container WindowContainerToken who's children should defer config notification.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction deferConfigToTransitionEnd(
- @NonNull WindowContainerToken container) {
- final Change change = getOrCreateChange(container.asBinder());
- change.mConfigAtTransitionEnd = true;
- return this;
- }
-
- /**
- * Sets the task as trimmable or not. This can be used to prevent the task from being trimmed by
- * recents. This attribute is set to true on task creation by default.
- *
- * @param isTrimmableFromRecents When {@code true}, task is set as trimmable from recents.
- * @hide
- */
- @NonNull
- public WindowContainerTransaction setTaskTrimmableFromRecents(
- @NonNull WindowContainerToken container,
- boolean isTrimmableFromRecents) {
- mHierarchyOps.add(
- HierarchyOp.createForSetTaskTrimmableFromRecents(container.asBinder(),
- isTrimmableFromRecents));
- return this;
- }
-
- /**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
* other in an unusable state. When false, other is left alone, but
@@ -1206,7 +1224,7 @@ public final class WindowContainerTransaction implements Parcelable {
@NonNull
public static final Creator<WindowContainerTransaction> CREATOR =
- new Creator<WindowContainerTransaction>() {
+ new Creator<>() {
@Override
public WindowContainerTransaction createFromParcel(@NonNull Parcel in) {
return new WindowContainerTransaction(in);
@@ -1227,19 +1245,17 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
public static final int CHANGE_PIP_CALLBACK = 1 << 2;
public static final int CHANGE_HIDDEN = 1 << 3;
- public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
- public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
- public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
- public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
- public static final int CHANGE_DRAG_RESIZING = 1 << 8;
- public static final int CHANGE_RELATIVE_BOUNDS = 1 << 9;
+ public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 4;
+ public static final int CHANGE_FORCE_NO_PIP = 1 << 5;
+ public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 6;
+ public static final int CHANGE_DRAG_RESIZING = 1 << 7;
+ public static final int CHANGE_RELATIVE_BOUNDS = 1 << 8;
@IntDef(flag = true, prefix = { "CHANGE_" }, value = {
CHANGE_FOCUSABLE,
CHANGE_BOUNDS_TRANSACTION,
CHANGE_PIP_CALLBACK,
CHANGE_HIDDEN,
- CHANGE_BOUNDS_TRANSACTION_RECT,
CHANGE_IGNORE_ORIENTATION_REQUEST,
CHANGE_FORCE_NO_PIP,
CHANGE_FORCE_TRANSLUCENT,
@@ -1262,7 +1278,6 @@ public final class WindowContainerTransaction implements Parcelable {
private Rect mPinnedBounds = null;
private SurfaceControl.Transaction mBoundsChangeTransaction = null;
- private Rect mBoundsChangeSurfaceBounds = null;
@Nullable
private Rect mRelativeBounds = null;
private boolean mConfigAtTransitionEnd = false;
@@ -1290,10 +1305,6 @@ public final class WindowContainerTransaction implements Parcelable {
mBoundsChangeTransaction =
SurfaceControl.Transaction.CREATOR.createFromParcel(in);
}
- if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) {
- mBoundsChangeSurfaceBounds = new Rect();
- mBoundsChangeSurfaceBounds.readFromParcel(in);
- }
if ((mChangeMask & Change.CHANGE_RELATIVE_BOUNDS) != 0) {
mRelativeBounds = new Rect();
mRelativeBounds.readFromParcel(in);
@@ -1342,10 +1353,6 @@ public final class WindowContainerTransaction implements Parcelable {
if (other.mWindowingMode >= WINDOWING_MODE_UNDEFINED) {
mWindowingMode = other.mWindowingMode;
}
- if (other.mBoundsChangeSurfaceBounds != null) {
- mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
- : new Rect(other.mBoundsChangeSurfaceBounds);
- }
if (other.mRelativeBounds != null) {
mRelativeBounds = transfer
? other.mRelativeBounds
@@ -1446,11 +1453,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
@Nullable
- public Rect getBoundsChangeSurfaceBounds() {
- return mBoundsChangeSurfaceBounds;
- }
-
- @Nullable
public Rect getRelativeBounds() {
return mRelativeBounds;
}
@@ -1529,9 +1531,6 @@ public final class WindowContainerTransaction implements Parcelable {
if (mBoundsChangeTransaction != null) {
mBoundsChangeTransaction.writeToParcel(dest, flags);
}
- if (mBoundsChangeSurfaceBounds != null) {
- mBoundsChangeSurfaceBounds.writeToParcel(dest, flags);
- }
if (mRelativeBounds != null) {
mRelativeBounds.writeToParcel(dest, flags);
}
diff --git a/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig b/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig
new file mode 100644
index 000000000000..bb66989b9946
--- /dev/null
+++ b/core/java/android/window/flags/device_state_auto_rotate_setting.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.window.flags"
+container: "system"
+
+flag {
+ name: "enable_device_state_auto_rotate_setting_logging"
+ namespace: "windowing_frontend"
+ description: "Enable device state auto rotate setting logging"
+ bug: "391147112"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_device_state_auto_rotate_setting_refactor"
+ namespace: "windowing_frontend"
+ description: "Enable refactored device state auto rotate setting logic"
+ bug: "350946537"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e35c3b80a58b..222088e8a8b9 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -583,3 +583,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_start_launch_transition_from_taskbar_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables starting a launch transition directly from the taskbar if desktop tasks are visible."
+ bug: "361366053"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 4b5adfcc2c9b..36219812c002 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -81,3 +81,10 @@ flag {
description: "Strict mode violation triggered by grace period usage"
bug: "384807495"
}
+
+flag {
+ name: "bal_clear_allowlist_duration"
+ namespace: "responsible_apis"
+ description: "Clear the allowlist duration when clearAllowBgActivityStarts is called"
+ bug: "322159724"
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index dc440e36ca0d..f49c5f1c2b0f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -84,7 +84,7 @@ public class BatteryStatsHistory {
private static final String TAG = "BatteryStatsHistory";
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- private static final int VERSION = 211;
+ private static final int VERSION = 212;
private static final String HISTORY_DIR = "battery-history";
private static final String FILE_SUFFIX = ".bh";
@@ -211,6 +211,8 @@ public class BatteryStatsHistory {
private final MonotonicClock mMonotonicClock;
// Monotonic time when we started writing to the history buffer
private long mHistoryBufferStartTime;
+ // Monotonic time when the last event was written to the history buffer
+ private long mHistoryMonotonicEndTime;
// Monotonically increasing size of written history
private long mMonotonicHistorySize;
private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
@@ -423,13 +425,22 @@ public class BatteryStatsHistory {
return file;
}
- void writeToParcel(Parcel out, boolean useBlobs) {
+ void writeToParcel(Parcel out, boolean useBlobs,
+ long preferredEarliestIncludedTimestampMs) {
Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.writeToParcel");
lock();
try {
final long start = SystemClock.uptimeMillis();
- out.writeInt(mHistoryFiles.size() - 1);
for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+ long monotonicEndTime = Long.MAX_VALUE;
+ if (i < mHistoryFiles.size() - 1) {
+ monotonicEndTime = mHistoryFiles.get(i + 1).monotonicTimeMs;
+ }
+
+ if (monotonicEndTime < preferredEarliestIncludedTimestampMs) {
+ continue;
+ }
+
AtomicFile file = mHistoryFiles.get(i).atomicFile;
byte[] raw = new byte[0];
try {
@@ -437,6 +448,8 @@ public class BatteryStatsHistory {
} catch (Exception e) {
Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
}
+
+ out.writeBoolean(true);
if (useBlobs) {
out.writeBlob(raw);
} else {
@@ -444,6 +457,7 @@ public class BatteryStatsHistory {
out.writeByteArray(raw);
}
}
+ out.writeBoolean(false);
if (DEBUG) {
Slog.d(TAG,
"writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
@@ -634,6 +648,7 @@ public class BatteryStatsHistory {
mWritableHistory = writableHistory;
if (mWritableHistory != null) {
mMutable = false;
+ mHistoryMonotonicEndTime = mWritableHistory.mHistoryMonotonicEndTime;
}
if (historyBuffer != null) {
@@ -937,6 +952,8 @@ public class BatteryStatsHistory {
}
// skip monotonic time field.
p.readLong();
+ // skip monotonic end time field
+ p.readLong();
// skip monotonic size field
p.readLong();
@@ -996,6 +1013,8 @@ public class BatteryStatsHistory {
}
// skip monotonic time field.
out.readLong();
+ // skip monotonic end time field
+ out.readLong();
// skip monotonic size field
out.readLong();
return true;
@@ -1024,6 +1043,7 @@ public class BatteryStatsHistory {
p.setDataPosition(0);
p.readInt(); // Skip the version field
long monotonicTime = p.readLong();
+ p.readLong(); // Skip monotonic end time field
p.readLong(); // Skip monotonic size field
p.setDataPosition(pos);
return monotonicTime;
@@ -1086,7 +1106,10 @@ public class BatteryStatsHistory {
public void writeToParcel(Parcel out) {
synchronized (this) {
writeHistoryBuffer(out);
- writeToParcel(out, false /* useBlobs */);
+ /* useBlobs */
+ if (mHistoryDir != null) {
+ mHistoryDir.writeToParcel(out, false /* useBlobs */, 0);
+ }
}
}
@@ -1096,16 +1119,13 @@ public class BatteryStatsHistory {
*
* @param out the output parcel
*/
- public void writeToBatteryUsageStatsParcel(Parcel out) {
+ public void writeToBatteryUsageStatsParcel(Parcel out, long preferredHistoryDurationMs) {
synchronized (this) {
out.writeBlob(mHistoryBuffer.marshall());
- writeToParcel(out, true /* useBlobs */);
- }
- }
-
- private void writeToParcel(Parcel out, boolean useBlobs) {
- if (mHistoryDir != null) {
- mHistoryDir.writeToParcel(out, useBlobs);
+ if (mHistoryDir != null) {
+ mHistoryDir.writeToParcel(out, true /* useBlobs */,
+ mHistoryMonotonicEndTime - preferredHistoryDurationMs);
+ }
}
}
@@ -1166,8 +1186,7 @@ public class BatteryStatsHistory {
private void readFromParcel(Parcel in, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
mHistoryParcels = new ArrayList<>();
- final int count = in.readInt();
- for (int i = 0; i < count; i++) {
+ while (in.readBoolean()) {
byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
if (temp == null || temp.length == 0) {
continue;
@@ -2081,6 +2100,8 @@ public class BatteryStatsHistory {
*/
@GuardedBy("this")
private void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
+ mHistoryMonotonicEndTime = cur.time;
+
if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
cur.writeToParcel(dest, 0);
@@ -2396,6 +2417,7 @@ public class BatteryStatsHistory {
}
mHistoryBufferStartTime = in.readLong();
+ mHistoryMonotonicEndTime = in.readLong();
mMonotonicHistorySize = in.readLong();
mHistoryBuffer.setDataSize(0);
@@ -2424,6 +2446,7 @@ public class BatteryStatsHistory {
private void writeHistoryBuffer(Parcel out) {
out.writeInt(BatteryStatsHistory.VERSION);
out.writeLong(mHistoryBufferStartTime);
+ out.writeLong(mHistoryMonotonicEndTime);
out.writeLong(mMonotonicHistorySize);
out.writeInt(mHistoryBuffer.dataSize());
if (DEBUG) {
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index b3ab5d3cd258..04ce9bcd7afd 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -105,6 +105,7 @@ public class ConversationLayout extends FrameLayout
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
private Person mUser;
private CharSequence mNameReplacement;
+ private CharSequence mSummarizedContent;
private boolean mIsCollapsed;
private ImageResolver mImageResolver;
private CachingIconView mConversationIconView;
@@ -397,7 +398,7 @@ public class ConversationLayout extends FrameLayout
*
* @param isCollapsed is it collapsed
*/
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
@@ -406,6 +407,15 @@ public class ConversationLayout extends FrameLayout
}
/**
+ * setDataAsync needs to do different stuff for the collapsed vs expanded view, so store the
+ * collapsed state early.
+ */
+ public Runnable setIsCollapsedAsync(boolean isCollapsed) {
+ mIsCollapsed = isCollapsed;
+ return () -> setIsCollapsed(isCollapsed);
+ }
+
+ /**
* Set conversation data
*
* @param extras Bundle contains conversation data
@@ -439,8 +449,16 @@ public class ConversationLayout extends FrameLayout
extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
- final List<MessagingMessage> newMessagingMessages =
- createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
+ List<MessagingMessage> newMessagingMessages;
+ mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
+ if (mSummarizedContent != null && mIsCollapsed) {
+ Notification.MessagingStyle.Message summary =
+ new Notification.MessagingStyle.Message(mSummarizedContent, 0, "");
+ newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
+ } else {
+ newMessagingMessages =
+ createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
+ }
final List<MessagingMessage> newHistoricMessagingMessages =
createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
@@ -463,7 +481,7 @@ public class ConversationLayout extends FrameLayout
return new MessagingData(user, showSpinner, unreadCount,
newHistoricMessagingMessages, newMessagingMessages, groups, senders,
- conversationHeaderData);
+ conversationHeaderData, mSummarizedContent);
}
/**
@@ -1622,6 +1640,9 @@ public class ConversationLayout extends FrameLayout
@Nullable
public CharSequence getConversationText() {
+ if (mSummarizedContent != null) {
+ return mSummarizedContent;
+ }
if (mMessages.isEmpty()) {
return null;
}
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
index fb1f28fb8ef3..cb5041efd10f 100644
--- a/core/java/com/android/internal/widget/MessagingData.java
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -32,6 +32,7 @@ final class MessagingData {
private final List<List<MessagingMessage>> mGroups;
private final List<Person> mSenders;
private final int mUnreadCount;
+ private final CharSequence mSummarization;
private ConversationHeaderData mConversationHeaderData;
@@ -41,8 +42,7 @@ final class MessagingData {
List<Person> senders) {
this(user, showSpinner, /* unreadCount= */0,
historicMessagingMessages, newMessagingMessages,
- groups,
- senders, null);
+ groups, senders, null, null);
}
MessagingData(Person user, boolean showSpinner,
@@ -51,7 +51,8 @@ final class MessagingData {
List<MessagingMessage> newMessagingMessages,
List<List<MessagingMessage>> groups,
List<Person> senders,
- @Nullable ConversationHeaderData conversationHeaderData) {
+ @Nullable ConversationHeaderData conversationHeaderData,
+ CharSequence summarization) {
mUser = user;
mShowSpinner = showSpinner;
mUnreadCount = unreadCount;
@@ -60,6 +61,7 @@ final class MessagingData {
mGroups = groups;
mSenders = senders;
mConversationHeaderData = conversationHeaderData;
+ mSummarization = summarization;
}
public Person getUser() {
@@ -94,4 +96,9 @@ final class MessagingData {
public ConversationHeaderData getConversationHeaderData() {
return mConversationHeaderData;
}
+
+ @Nullable
+ public CharSequence getSummarization() {
+ return mSummarization;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index a59ee77cc693..c7f22836dd93 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -24,7 +24,7 @@ import java.util.ArrayList;
import java.util.Objects;
/**
- * A message of a {@link MessagingLayout}.
+ * A message or summary of a {@link MessagingLayout}.
*/
public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 4ece81c24edc..30dcc67d9ce5 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -132,6 +132,8 @@ public final class NotificationProgressDrawable extends Drawable {
final float centerY = (float) getBounds().centerY();
final int numParts = mParts.size();
+ final float pointTop = Math.round(centerY - pointRadius);
+ final float pointBottom = Math.round(centerY + pointRadius);
for (int iPart = 0; iPart < numParts; iPart++) {
final DrawablePart part = mParts.get(iPart);
final float start = left + part.mStart;
@@ -146,12 +148,13 @@ public final class NotificationProgressDrawable extends Drawable {
mFillPaint.setColor(segment.mColor);
- mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY);
+ mSegRectF.set(Math.round(start), Math.round(centerY - radiusY), Math.round(end),
+ Math.round(centerY + radiusY));
canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint);
} else if (part instanceof DrawablePoint point) {
// TODO: b/367804171 - actually use a vector asset for the default point
// rather than drawing it as a box?
- mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius);
+ mPointRectF.set(Math.round(start), pointTop, Math.round(end), pointBottom);
final float inset = mState.mPointRectInset;
final float cornerRadius = mState.mPointRectCornerRadius;
mPointRectF.inset(inset, inset);
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index 2cd4f0362306..52d51539867d 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -21,6 +21,7 @@ import android.os.Bundle;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
@@ -45,9 +46,11 @@ import java.util.stream.Stream;
*/
public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibility {
private final CoreDocument mDocument;
+ private final RemoteContext mRemoteContext;
- public CoreDocumentAccessibility(CoreDocument document) {
+ public CoreDocumentAccessibility(CoreDocument document, RemoteContext remoteContext) {
this.mDocument = document;
+ this.mRemoteContext = remoteContext;
}
@Nullable
@@ -95,7 +98,7 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
@Override
public boolean performAction(Component component, int action, Bundle arguments) {
if (action == ACTION_CLICK) {
- mDocument.performClick(component.getComponentId());
+ mDocument.performClick(mRemoteContext, component.getComponentId());
return true;
} else {
return false;
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
index 010253e9cb95..975383ee36b4 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeAccessibilityRegistrar.java
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import android.view.View;
import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
/**
* Trivial wrapper for calling setAccessibilityDelegate on a View. This exists primarily because the
@@ -31,7 +32,8 @@ public class PlatformRemoteComposeAccessibilityRegistrar
View player, @NonNull CoreDocument coreDocument) {
return new PlatformRemoteComposeTouchHelper(
player,
- new CoreDocumentAccessibility(coreDocument),
+ new CoreDocumentAccessibility(
+ coreDocument, ((RemoteContextAware) player).getRemoteContext()),
new AndroidPlatformSemanticNodeApplier());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index 43118a0800fb..c8474b19058f 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -28,6 +28,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.widget.ExploreByTouchHelper;
import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySemantics;
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent.Mode;
@@ -55,7 +56,8 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
View player, @NonNull CoreDocument coreDocument) {
return new PlatformRemoteComposeTouchHelper(
player,
- new CoreDocumentAccessibility(coreDocument),
+ new CoreDocumentAccessibility(
+ coreDocument, ((RemoteContextAware) player).getRemoteContext()),
new AndroidPlatformSemanticNodeApplier());
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index f5f4e4332d28..0cfaf5592d6f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -62,7 +62,7 @@ public class CoreDocument {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.3f;
+ static final float BUILD = 0.4f;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
@@ -860,16 +860,22 @@ public class CoreDocument {
*
* @param id the click area id
*/
- public void performClick(int id) {
+ public void performClick(@NonNull RemoteContext context, int id) {
for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.mId == id) {
warnClickListeners(clickArea);
return;
}
}
+
for (IdActionCallback listener : mIdActionListeners) {
listener.onAction(id, "");
}
+
+ Component component = getComponent(id);
+ if (component != null) {
+ component.onClick(context, this, -1, -1);
+ }
}
/** Warn click listeners when a click area is activated */
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 0b6a3c415e4a..3760af2f7460 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.core;
import android.annotation.NonNull;
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.BitmapFontData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
@@ -30,6 +31,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
@@ -45,6 +47,8 @@ import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -147,12 +151,14 @@ public class Operations {
public static final int DATA_BITMAP = 101;
public static final int DATA_SHADER = 45;
public static final int DATA_TEXT = 102;
+ public static final int DATA_BITMAP_FONT = 167;
///////////////////////////// =====================
public static final int CLIP_PATH = 38;
public static final int CLIP_RECT = 39;
public static final int PAINT_VALUES = 40;
public static final int DRAW_RECT = 42;
+ public static final int DRAW_BITMAP_FONT_TEXT_RUN = 48;
public static final int DRAW_TEXT_RUN = 43;
public static final int DRAW_CIRCLE = 46;
public static final int DRAW_LINE = 47;
@@ -196,11 +202,13 @@ public class Operations {
public static final int PATH_TWEEN = 158;
public static final int PATH_CREATE = 159;
public static final int PATH_ADD = 160;
- public static final int PARTICLE_CREATE = 161;
+ public static final int PARTICLE_DEFINE = 161;
public static final int PARTICLE_PROCESS = 162;
public static final int PARTICLE_LOOP = 163;
public static final int IMPULSE_START = 164;
public static final int IMPULSE_PROCESS = 165;
+ public static final int FUNCTION_CALL = 166;
+ public static final int FUNCTION_DEFINE = 168;
///////////////////////////////////////// ======================
@@ -276,6 +284,7 @@ public class Operations {
map.put(HEADER, Header::read);
map.put(DRAW_BITMAP_INT, DrawBitmapInt::read);
map.put(DATA_BITMAP, BitmapData::read);
+ map.put(DATA_BITMAP_FONT, BitmapFontData::read);
map.put(DATA_TEXT, TextData::read);
map.put(THEME, Theme::read);
map.put(CLICK_AREA, ClickArea::read);
@@ -292,6 +301,7 @@ public class Operations {
map.put(DRAW_ROUND_RECT, DrawRoundRect::read);
map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath::read);
map.put(DRAW_TEXT_RUN, DrawText::read);
+ map.put(DRAW_BITMAP_FONT_TEXT_RUN, DrawBitmapFontText::read);
map.put(DRAW_TWEEN_PATH, DrawTweenPath::read);
map.put(DATA_PATH, PathData::read);
map.put(PAINT_VALUES, PaintData::read);
@@ -389,8 +399,10 @@ public class Operations {
map.put(PATH_ADD, PathAppend::read);
map.put(IMPULSE_START, ImpulseOperation::read);
map.put(IMPULSE_PROCESS, ImpulseProcess::read);
- map.put(PARTICLE_CREATE, ParticlesCreate::read);
+ map.put(PARTICLE_DEFINE, ParticlesCreate::read);
map.put(PARTICLE_LOOP, ParticlesLoop::read);
+ map.put(FUNCTION_CALL, FloatFunctionCall::read);
+ map.put(FUNCTION_DEFINE, FloatFunctionDefine::read);
map.put(ACCESSIBILITY_SEMANTICS, CoreSemantics::read);
// map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 1cb8fefde80c..f83ecef1074d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.BitmapFontData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
@@ -33,6 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
@@ -48,6 +50,8 @@ import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
+import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -557,6 +561,18 @@ public class RemoteComposeBuffer {
}
/**
+ * Records a bitmap font and returns an ID.
+ *
+ * @param glyphs The glyphs that define the bitmap font
+ * @return id of the BitmapFont
+ */
+ public int addBitmapFont(BitmapFontData.Glyph[] glyphs) {
+ int id = mRemoteComposeState.nextId();
+ BitmapFontData.apply(mBuffer, id, glyphs);
+ return id;
+ }
+
+ /**
* This defines the name of the bitmap given the id.
*
* @param id of the Bitmap
@@ -825,6 +841,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Draw the text with a bitmap font, with origin at (x,y). The origin is interpreted based on
+ * the Align setting in the paint.
+ *
+ * @param textId The text to be drawn
+ * @param bitmapFontId The id of the bitmap font to draw with
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ */
+ public void addDrawBitmapFontTextRun(
+ int textId, int bitmapFontId, int start, int end, float x, float y) {
+ DrawBitmapFontText.apply(mBuffer, textId, bitmapFontId, start, end, x, y);
+ }
+
+ /**
* Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
* The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
* way that a panning factor of (0.0, 0.0) would center the text at (x, y)
@@ -1060,6 +1092,14 @@ public class RemoteComposeBuffer {
return "v1.0";
}
+ /**
+ * Initialize a buffer from a file
+ *
+ * @param path the file path
+ * @param remoteComposeState the associated state
+ * @return the RemoteComposeBuffer
+ * @throws IOException
+ */
@NonNull
public static RemoteComposeBuffer fromFile(
@NonNull String path, @NonNull RemoteComposeState remoteComposeState)
@@ -1134,11 +1174,24 @@ public class RemoteComposeBuffer {
}
}
+ /**
+ * Read the content of the file into the buffer
+ *
+ * @param file a target file
+ * @param buffer a RemoteComposeBuffer
+ * @throws IOException
+ */
static void read(@NonNull File file, @NonNull RemoteComposeBuffer buffer) throws IOException {
FileInputStream fd = new FileInputStream(file);
read(fd, buffer);
}
+ /**
+ * Initialize a buffer from an input stream
+ *
+ * @param fd the input stream
+ * @param buffer a RemoteComposeBuffer
+ */
public static void read(@NonNull InputStream fd, @NonNull RemoteComposeBuffer buffer) {
try {
byte[] bytes = readAllBytes(fd);
@@ -1150,6 +1203,13 @@ public class RemoteComposeBuffer {
}
}
+ /**
+ * Load a byte buffer from the input stream
+ *
+ * @param is the input stream
+ * @return a byte buffer containing the input stream content
+ * @throws IOException
+ */
private static byte[] readAllBytes(@NonNull InputStream is) throws IOException {
byte[] buff = new byte[32 * 1024]; // moderate size buff to start
int red = 0;
@@ -1684,7 +1744,27 @@ public class RemoteComposeBuffer {
* @return id of the color (color ids are short)
*/
public short addColorExpression(int alpha, float hue, float sat, float value) {
- ColorExpression c = new ColorExpression(0, alpha, hue, sat, value);
+ ColorExpression c =
+ new ColorExpression(0, ColorExpression.HSV_MODE, alpha, hue, sat, value);
+ short id = (short) mRemoteComposeState.cacheData(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Color calculated by Alpha, Red, Green and Blue. (as floats they can be variables used to
+ * create color transitions)
+ *
+ * @param alpha the alpha value of the color
+ * @param red the red component of the color
+ * @param green the green component of the color
+ * @param blue the blue component of the color
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(float alpha, float red, float green, float blue) {
+ ColorExpression c =
+ new ColorExpression(0, ColorExpression.ARGB_MODE, alpha, red, green, blue);
short id = (short) mRemoteComposeState.cacheData(c);
c.mId = id;
c.write(mBuffer);
@@ -2179,10 +2259,21 @@ public class RemoteComposeBuffer {
textAlign);
}
+ /**
+ * Returns the next available id for the given type
+ *
+ * @param type the type of the value
+ * @return a unique id
+ */
public int createID(int type) {
return mRemoteComposeState.nextId(type);
}
+ /**
+ * Returns the next available id
+ *
+ * @return a unique id
+ */
public int nextId() {
return mRemoteComposeState.nextId();
}
@@ -2243,4 +2334,27 @@ public class RemoteComposeBuffer {
public void addParticleLoopEnd() {
ContainerEnd.apply(mBuffer);
}
+
+ /**
+ * @param fid The id of the function
+ * @param args The arguments of the function
+ */
+ public void defineFloatFunction(int fid, int[] args) {
+ FloatFunctionDefine.apply(mBuffer, fid, args);
+ }
+
+ /** end the definition of the function */
+ public void addEndFloatFunctionDef() {
+ ContainerEnd.apply(mBuffer);
+ }
+
+ /**
+ * add a function call
+ *
+ * @param id the id of the function to call
+ * @param args the arguments of the function
+ */
+ public void callFloatFunction(int id, float[] args) {
+ FloatFunctionCall.apply(mBuffer, id, args);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java
new file mode 100644
index 000000000000..bf9a8c02d525
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContextAware.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core;
+
+/**
+ * This interface defines a contract for objects that are aware of a {@link RemoteContext}.
+ *
+ * <p>PlayerViews should implement to provide access to the RemoteContext.
+ */
+public interface RemoteContextAware {
+
+ /**
+ * Returns the remote context
+ *
+ * @return a RemoteContext
+ */
+ RemoteContext getRemoteContext();
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java
new file mode 100644
index 000000000000..cbd30dc21caf
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapFontData.java
@@ -0,0 +1,219 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Operation to deal with bitmap font data. */
+public class BitmapFontData extends Operation {
+ private static final int OP_CODE = Operations.DATA_BITMAP_FONT;
+ private static final String CLASS_NAME = "BitmapFontData";
+
+ int mId;
+
+ // Sorted in order of decreasing mChars length.
+ @NonNull Glyph[] mFontGlyphs;
+
+ /**
+ * A bitmap font is comprised of a collection of Glyphs. Note each Glyph has its own bitmap
+ * rather than using a texture atlas.
+ */
+ public static class Glyph {
+ /** The character(s) this glyph represents. */
+ public String mChars;
+
+ /** The id of the bitmap for this glyph, or -1 for space. */
+ public int mBitmapId;
+
+ /** The margin in pixels to the left of the glyph bitmap. */
+ public short mMarginLeft;
+
+ /** The margin in pixels above of the glyph bitmap. */
+ public short mMarginTop;
+
+ /** The margin in pixels to the right of the glyph bitmap. */
+ public short mMarginRight;
+
+ /** The margin in pixels below the glyph bitmap. */
+ public short mMarginBottom;
+
+ public short mBitmapWidth;
+ public short mBitmapHeight;
+
+ public Glyph() {}
+
+ public Glyph(
+ String chars,
+ int bitmapId,
+ short marginLeft,
+ short marginTop,
+ short marginRight,
+ short marginBottom,
+ short width,
+ short height) {
+ mChars = chars;
+ mBitmapId = bitmapId;
+ mMarginLeft = marginLeft;
+ mMarginTop = marginTop;
+ mMarginRight = marginRight;
+ mMarginBottom = marginBottom;
+ mBitmapWidth = width;
+ mBitmapHeight = height;
+ }
+ }
+
+ /**
+ * create a bitmap font structure.
+ *
+ * @param id the id of the bitmap font
+ * @param fontGlyphs the glyphs that define the bitmap font
+ */
+ public BitmapFontData(int id, @NonNull Glyph[] fontGlyphs) {
+ mId = id;
+ mFontGlyphs = fontGlyphs;
+
+ // Sort in order of decreasing mChars length.
+ Arrays.sort(mFontGlyphs, (o1, o2) -> o2.mChars.length() - o1.mChars.length());
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mId, mFontGlyphs);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "BITMAP FONT DATA " + mId;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Add the image to the document
+ *
+ * @param buffer document to write to
+ * @param id the id the bitmap font will be stored under
+ * @param glyphs glyph metadata
+ */
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull Glyph[] glyphs) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(glyphs.length);
+ for (Glyph element : glyphs) {
+ buffer.writeUTF8(element.mChars);
+ buffer.writeInt(element.mBitmapId);
+ buffer.writeShort(element.mMarginLeft);
+ buffer.writeShort(element.mMarginTop);
+ buffer.writeShort(element.mMarginRight);
+ buffer.writeShort(element.mMarginBottom);
+ buffer.writeShort(element.mBitmapWidth);
+ buffer.writeShort(element.mBitmapHeight);
+ }
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int id = buffer.readInt();
+ int numGlyphElements = buffer.readInt();
+ Glyph[] glyphs = new Glyph[numGlyphElements];
+ for (int i = 0; i < numGlyphElements; i++) {
+ glyphs[i] = new Glyph();
+ glyphs[i].mChars = buffer.readUTF8();
+ glyphs[i].mBitmapId = buffer.readInt();
+ glyphs[i].mMarginLeft = (short) buffer.readShort();
+ glyphs[i].mMarginTop = (short) buffer.readShort();
+ glyphs[i].mMarginRight = (short) buffer.readShort();
+ glyphs[i].mMarginBottom = (short) buffer.readShort();
+ glyphs[i].mBitmapWidth = (short) buffer.readShort();
+ glyphs[i].mBitmapHeight = (short) buffer.readShort();
+ }
+
+ operations.add(new BitmapFontData(id, glyphs));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Bitmap font data")
+ .field(DocumentedOperation.INT, "id", "id of bitmap font data")
+ .field(INT_ARRAY, "glyphNodes", "list used to greedily convert strings into glyphs")
+ .field(INT_ARRAY, "glyphElements", "");
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ context.putObject(mId, this);
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ /** Finds the largest glyph matching the string at the specified offset, or returns null. */
+ @Nullable
+ public Glyph lookupGlyph(String string, int offset) {
+ // Since mFontGlyphs is sorted on decreasing size, it will match the longest items first.
+ // It is expected that the mFontGlyphs array will be fairly small.
+ for (Glyph glyph : mFontGlyphs) {
+ if (string.startsWith(glyph.mChars, offset)) {
+ return glyph;
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index b385ecd9e5f7..73f99ccb4405 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -38,7 +38,13 @@ public class ColorExpression extends Operation implements VariableSupport {
private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
private static final String CLASS_NAME = "ColorExpression";
public int mId;
+
+ /**
+ * Mode of the color expression 0 = two colors and a tween 1 = color1 is a colorID. 2 color2 is
+ * a colorID. 3 = color1 & color2 are ids 4 = H S V mode 5 = RGB mode 6 = ARGB mode
+ */
int mMode;
+
public int mColor1;
public int mColor2;
public float mTween = 0.0f;
@@ -51,11 +57,49 @@ public class ColorExpression extends Operation implements VariableSupport {
public float mOutValue = 0;
public int mAlpha = 0xFF; // only used in hsv mode
+ private float mArgbAlpha = 0.0f;
+ private float mArgbRed = 0.0f;
+ private float mArgbGreen = 0.0f;
+ private float mArgbBlue = 0.0f;
+
+ private float mOutArgbAlpha = 0.0f;
+ private float mOutArgbRed = 0.0f;
+ private float mOutArgbGreen = 0.0f;
+ private float mOutArgbBlue = 0.0f;
+
public float mOutTween = 0.0f;
public int mOutColor1;
public int mOutColor2;
- public static final int HSV_MODE = 4;
+ /** COLOR_COLOR_INTERPOLATE */
+ public static final byte COLOR_COLOR_INTERPOLATE = 0;
+
+ /** COLOR_ID_INTERPOLATE */
+ public static final byte ID_COLOR_INTERPOLATE = 1;
+
+ /** ID_COLOR_INTERPOLATE */
+ public static final byte COLOR_ID_INTERPOLATE = 2;
+
+ /** ID_ID_INTERPOLATE */
+ public static final byte ID_ID_INTERPOLATE = 3;
+
+ /** H S V mode */
+ public static final byte HSV_MODE = 4;
+
+ /** ARGB mode */
+ public static final byte ARGB_MODE = 5;
+
+ /** ARGB mode with a being an id */
+ public static final byte IDARGB_MODE = 6;
+
+ /**
+ * Create a new ColorExpression object
+ *
+ * @param id the id of the color
+ * @param hue the hue of the color
+ * @param sat the saturation of the color
+ * @param value the value of the color
+ */
public ColorExpression(int id, float hue, float sat, float value) {
mMode = HSV_MODE;
mAlpha = 0xFF;
@@ -67,7 +111,21 @@ public class ColorExpression extends Operation implements VariableSupport {
mTween = value;
}
- public ColorExpression(int id, int alpha, float hue, float sat, float value) {
+ /**
+ * Create a new ColorExpression object based on HSV
+ *
+ * @param id id of the color
+ * @param mode the mode of the color
+ * @param alpha the alpha of the color
+ * @param hue the hue of the color
+ * @param sat the saturation of the color
+ * @param value the value (brightness) of the color
+ */
+ public ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value) {
+ if (mode != HSV_MODE) {
+ throw new RuntimeException("Invalid mode " + mode);
+ }
+ mId = id;
mMode = HSV_MODE;
mAlpha = alpha;
mOutHue = mHue = hue;
@@ -78,6 +136,15 @@ public class ColorExpression extends Operation implements VariableSupport {
mTween = value;
}
+ /**
+ * Create a new ColorExpression object based interpolationg two colors
+ *
+ * @param id the id of the color
+ * @param mode the type of mode (are colors ids or actual values)
+ * @param color1 the first color to use
+ * @param color2 the second color to use
+ * @param tween the value to use to interpolate between the two colors
+ */
public ColorExpression(int id, int mode, int color1, int color2, float tween) {
this.mId = id;
this.mMode = mode & 0xFF;
@@ -95,6 +162,28 @@ public class ColorExpression extends Operation implements VariableSupport {
this.mOutColor2 = color2;
}
+ /**
+ * Create a new ColorExpression object based on ARGB
+ *
+ * @param id the id of the color
+ * @param mode the mode must be ARGB_MODE
+ * @param alpha the alpha value of the color
+ * @param red the red of component the color
+ * @param green the greej component of the color
+ * @param blue the blue of component the color
+ */
+ public ColorExpression(int id, byte mode, float alpha, float red, float green, float blue) {
+ if (mode != ARGB_MODE) {
+ throw new RuntimeException("Invalid mode " + mode);
+ }
+ mMode = ARGB_MODE;
+ mId = id;
+ mOutArgbAlpha = mArgbAlpha = alpha;
+ mOutArgbRed = mArgbRed = red;
+ mOutArgbGreen = mArgbGreen = green;
+ mOutArgbBlue = mArgbBlue = blue;
+ }
+
@Override
public void updateVariables(@NonNull RemoteContext context) {
if (mMode == 4) {
@@ -108,6 +197,20 @@ public class ColorExpression extends Operation implements VariableSupport {
mOutValue = context.getFloat(Utils.idFromNan(mValue));
}
}
+ if (mMode == ARGB_MODE) {
+ if (Float.isNaN(mArgbAlpha)) {
+ mOutArgbAlpha = context.getFloat(Utils.idFromNan(mArgbAlpha));
+ }
+ if (Float.isNaN(mArgbRed)) {
+ mOutArgbRed = context.getFloat(Utils.idFromNan(mArgbRed));
+ }
+ if (Float.isNaN(mArgbGreen)) {
+ mOutArgbGreen = context.getFloat(Utils.idFromNan(mArgbGreen));
+ }
+ if (Float.isNaN(mArgbBlue)) {
+ mOutArgbBlue = context.getFloat(Utils.idFromNan(mArgbBlue));
+ }
+ }
if (Float.isNaN(mTween)) {
mOutTween = context.getFloat(Utils.idFromNan(mTween));
}
@@ -146,13 +249,21 @@ public class ColorExpression extends Operation implements VariableSupport {
@Override
public void apply(@NonNull RemoteContext context) {
- if (mMode == 4) {
+ if (mMode == HSV_MODE) {
context.loadColor(
mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
return;
}
+ if (mMode == ARGB_MODE) {
+ context.loadColor(
+ mId, Utils.toARGB(mOutArgbAlpha, mOutArgbRed, mOutArgbGreen, mOutArgbBlue));
+ return;
+ }
if (mOutTween == 0.0) {
- context.loadColor(mId, mColor1);
+ if ((mMode & 1) == 1) {
+ mOutColor1 = context.getColor(mColor1);
+ }
+ context.loadColor(mId, mOutColor1);
} else {
if ((mMode & 1) == 1) {
mOutColor1 = context.getColor(mColor1);
@@ -167,14 +278,36 @@ public class ColorExpression extends Operation implements VariableSupport {
@Override
public void write(@NonNull WireBuffer buffer) {
- int mode = mMode | (mAlpha << 16);
- apply(buffer, mId, mode, mColor1, mColor2, mTween);
+ int mode;
+ switch (mMode) {
+ case ARGB_MODE:
+ apply(buffer, mId, mArgbAlpha, mArgbRed, mArgbGreen, mArgbBlue);
+ break;
+
+ case HSV_MODE:
+ mOutValue = mValue;
+ mColor1 = Float.floatToRawIntBits(mHue);
+ mColor2 = Float.floatToRawIntBits(mSat);
+ mode = mMode | (mAlpha << 16);
+ apply(buffer, mId, mode, mColor1, mColor2, mTween);
+
+ break;
+ case COLOR_ID_INTERPOLATE:
+ case ID_COLOR_INTERPOLATE:
+ case ID_ID_INTERPOLATE:
+ case COLOR_COLOR_INTERPOLATE:
+ apply(buffer, mId, mMode, mColor1, mColor2, mTween);
+
+ break;
+ default:
+ throw new RuntimeException("Invalid mode ");
+ }
}
@NonNull
@Override
public String toString() {
- if (mMode == 4) {
+ if (mMode == HSV_MODE) {
return "ColorExpression["
+ mId
+ "] = hsv ("
@@ -185,7 +318,20 @@ public class ColorExpression extends Operation implements VariableSupport {
+ Utils.floatToString(mValue)
+ ")";
}
-
+ Utils.log(" ColorExpression toString" + mId + " " + mMode);
+ if (mMode == ARGB_MODE) {
+ return "ColorExpression["
+ + mId
+ + "] = rgb ("
+ + Utils.floatToString(mArgbAlpha)
+ + ", "
+ + Utils.floatToString(mArgbRed)
+ + ", "
+ + Utils.floatToString(mArgbGreen)
+ + ", "
+ + Utils.floatToString(mArgbRed)
+ + ")";
+ }
String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
return "ColorExpression["
@@ -230,12 +376,38 @@ public class ColorExpression extends Operation implements VariableSupport {
*/
public static void apply(
@NonNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
+ apply(buffer, id, mode, color1, color2, Float.floatToRawIntBits(tween));
+ }
+
+ /**
+ * Call to write a ColorExpression object on the buffer
+ *
+ * @param buffer
+ * @param id of the ColorExpression object
+ * @param alpha
+ * @param red
+ * @param green
+ * @param blue
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer, int id, float alpha, float red, float green, float blue) {
+ int param1 = (Float.isNaN(alpha)) ? IDARGB_MODE : ARGB_MODE;
+ param1 |=
+ (Float.isNaN(alpha)) ? Utils.idFromNan(alpha) << 16 : ((int) (alpha * 1024)) << 16;
+ int param2 = Float.floatToRawIntBits(red);
+ int param3 = Float.floatToRawIntBits(green);
+ int param4 = Float.floatToRawIntBits(blue);
+ apply(buffer, id, param1, param2, param3, param4);
+ }
+
+ private static void apply(
+ @NonNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4) {
buffer.start(OP_CODE);
buffer.writeInt(id);
- buffer.writeInt(mode);
- buffer.writeInt(color1);
- buffer.writeInt(color2);
- buffer.writeFloat(tween);
+ buffer.writeInt(param1);
+ buffer.writeInt(param2);
+ buffer.writeInt(param3);
+ buffer.writeInt(param4);
}
/**
@@ -246,12 +418,48 @@ public class ColorExpression extends Operation implements VariableSupport {
*/
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
- int mode = buffer.readInt();
- int color1 = buffer.readInt();
- int color2 = buffer.readInt();
- float tween = buffer.readFloat();
-
- operations.add(new ColorExpression(id, mode, color1, color2, tween));
+ int param1 = buffer.readInt();
+ int param2 = buffer.readInt();
+ int param3 = buffer.readInt();
+ int param4 = buffer.readInt();
+ int mode = param1 & 0xFF;
+ float alpha;
+ float red;
+ float green;
+ float blue;
+ switch (mode) {
+ case IDARGB_MODE:
+ alpha = Utils.asNan(param1 >> 16);
+ red = Float.intBitsToFloat(param2);
+ green = Float.intBitsToFloat(param3);
+ blue = Float.intBitsToFloat(param4);
+ operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
+ break;
+ case ARGB_MODE:
+ alpha = (param1 >> 16) / 1024.0f;
+ red = Float.intBitsToFloat(param2);
+ green = Float.intBitsToFloat(param3);
+ blue = Float.intBitsToFloat(param4);
+ operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
+ break;
+ case HSV_MODE:
+ alpha = (param1 >> 16) / 1024.0f;
+ float hue = Float.intBitsToFloat(param2);
+ float sat = Float.intBitsToFloat(param3);
+ float value = Float.intBitsToFloat(param4);
+ operations.add(new ColorExpression(id, HSV_MODE, (param1 >> 16), hue, sat, value));
+ break;
+ case COLOR_ID_INTERPOLATE:
+ case ID_COLOR_INTERPOLATE:
+ case ID_ID_INTERPOLATE:
+ case COLOR_COLOR_INTERPOLATE:
+ operations.add(
+ new ColorExpression(
+ id, mode, param2, param3, Float.intBitsToFloat(param4)));
+ break;
+ default:
+ throw new RuntimeException("Invalid mode " + mode);
+ }
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index 7f1ba6f94065..411353bd3509 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -31,8 +31,8 @@ import java.util.List;
/** Base class for commands that take 3 float */
public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
@NonNull protected String mName = "DrawRectBase";
- protected float mV1;
- protected float mV2;
+ float mV1;
+ float mV2;
float mValue1;
float mValue2;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java
new file mode 100644
index 000000000000..258988e8b00a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapFontText.java
@@ -0,0 +1,232 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Draw Text */
+public class DrawBitmapFontText extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.DRAW_BITMAP_FONT_TEXT_RUN;
+ private static final String CLASS_NAME = "DrawBitmapFontText";
+ int mTextID;
+ int mBitmapFontID;
+ int mStart;
+ int mEnd;
+ float mX;
+ float mY;
+ float mOutX;
+ float mOutY;
+
+ public DrawBitmapFontText(int textID, int bitmapFontID, int start, int end, float x, float y) {
+ mTextID = textID;
+ mBitmapFontID = bitmapFontID;
+ mStart = start;
+ mEnd = end;
+ mOutX = mX = x;
+ mOutY = mY = y;
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ if (Float.isNaN(mX)) {
+ context.listensTo(Utils.idFromNan(mX), this);
+ }
+ if (Float.isNaN(mY)) {
+ context.listensTo(Utils.idFromNan(mY), this);
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mTextID, mBitmapFontID, mStart, mEnd, mX, mY);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "DrawBitmapFontText ["
+ + mTextID
+ + "] "
+ + mBitmapFontID
+ + ", "
+ + mStart
+ + ", "
+ + mEnd
+ + ", "
+ + floatToString(mX, mOutX)
+ + ", "
+ + floatToString(mY, mOutY);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int text = buffer.readInt();
+ int bitmapFont = buffer.readInt();
+ int start = buffer.readInt();
+ int end = buffer.readInt();
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ DrawBitmapFontText op = new DrawBitmapFontText(text, bitmapFont, start, end, x, y);
+
+ operations.add(op);
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write the command to the buffer
+ * @param textID id of the text
+ * @param bitmapFontID id of the bitmap font
+ * @param start Start position
+ * @param end end position
+ * @param x position of where to draw
+ * @param y position of where to draw
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int textID,
+ int bitmapFontID,
+ int start,
+ int end,
+ float x,
+ float y) {
+ buffer.start(Operations.DRAW_BITMAP_FONT_TEXT_RUN);
+ buffer.writeInt(textID);
+ buffer.writeInt(bitmapFontID);
+ buffer.writeInt(start);
+ buffer.writeInt(end);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Draw Operations", id(), CLASS_NAME)
+ .description("Draw a run of bitmap font text, all in a single direction")
+ .field(DocumentedOperation.INT, "textId", "id of bitmap")
+ .field(DocumentedOperation.INT, "bitmapFontId", "id of the bitmap font")
+ .field(
+ DocumentedOperation.INT,
+ "start",
+ "The start of the text to render. -1=end of string")
+ .field(DocumentedOperation.INT, "end", "The end of the text to render")
+ .field(
+ DocumentedOperation.INT,
+ "contextStart",
+ "the index of the start of the shaping context")
+ .field(
+ DocumentedOperation.INT,
+ "contextEnd",
+ "the index of the end of the shaping context")
+ .field(DocumentedOperation.FLOAT, "x", "The x position at which to draw the text")
+ .field(DocumentedOperation.FLOAT, "y", "The y position at which to draw the text")
+ .field(DocumentedOperation.BOOLEAN, "RTL", "Whether the run is in RTL direction");
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
+ String textToPaint = remoteContext.getText(mTextID);
+ if (textToPaint == null) {
+ return;
+ }
+ if (mEnd == -1) {
+ if (mStart != 0) {
+ textToPaint = textToPaint.substring(mStart);
+ }
+ } else if (mEnd > textToPaint.length()) {
+ textToPaint = textToPaint.substring(mStart);
+ } else {
+ textToPaint = textToPaint.substring(mStart, mEnd);
+ }
+
+ BitmapFontData bitmapFont = (BitmapFontData) remoteContext.getObject(mBitmapFontID);
+ if (bitmapFont == null) {
+ return;
+ }
+
+ float xPos = mX;
+ int pos = 0;
+ while (pos < textToPaint.length()) {
+ BitmapFontData.Glyph glyph = bitmapFont.lookupGlyph(textToPaint, pos);
+ if (glyph == null) {
+ pos++;
+ continue;
+ }
+
+ pos += glyph.mChars.length();
+ if (glyph.mBitmapId == -1) {
+ // Space is represented by a glyph of -1.
+ xPos += glyph.mMarginLeft + glyph.mMarginRight;
+ continue;
+ }
+
+ xPos += glyph.mMarginLeft;
+ float xPos2 = xPos + glyph.mBitmapWidth;
+ context.drawBitmap(
+ glyph.mBitmapId, xPos, mY + glyph.mMarginTop, xPos2, mY + glyph.mBitmapHeight);
+ xPos = xPos2 + glyph.mMarginRight;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java
new file mode 100644
index 000000000000..eccc00a18308
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionCall.java
@@ -0,0 +1,185 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** This provides the command to call a floatfunction defined in floatfunction */
+public class FloatFunctionCall extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.FUNCTION_CALL;
+ private static final String CLASS_NAME = "FunctionCall";
+ private final int mId;
+ private final float[] mArgs;
+ private final float[] mOutArgs;
+
+ FloatFunctionDefine mFunction;
+
+ @NonNull private ArrayList<Operation> mList = new ArrayList<>();
+
+ @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+ /**
+ * Create a new FloatFunctionCall operation
+ *
+ * @param id The function to call
+ * @param args the arguments to call it with
+ */
+ public FloatFunctionCall(int id, float[] args) {
+ mId = id;
+ mArgs = args;
+ if (args != null) {
+ mOutArgs = new float[args.length];
+ System.arraycopy(args, 0, mOutArgs, 0, args.length);
+ } else {
+ mOutArgs = null;
+ }
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ if (mOutArgs != null) {
+ for (int i = 0; i < mArgs.length; i++) {
+ float v = mArgs[i];
+ mOutArgs[i] =
+ (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v))
+ ? context.getFloat(Utils.idFromNan(v))
+ : v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ mFunction = (FloatFunctionDefine) context.getObject(mId);
+ if (mArgs != null) {
+ for (int i = 0; i < mArgs.length; i++) {
+ float v = mArgs[i];
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mId, mArgs);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ String str = "callFunction[" + Utils.idString(mId) + "] ";
+ for (int i = 0; i < mArgs.length; i++) {
+ str += ((i == 0) ? "" : " ,") + Utils.floatToString(mArgs[i], mOutArgs[i]);
+ }
+ return str;
+ }
+
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the function to call
+ * @param args the arguments to call the function with
+ */
+ public static void apply(@NonNull WireBuffer buffer, int id, @Nullable float[] args) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ if (args != null) {
+ buffer.writeInt(args.length);
+ for (int i = 0; i < args.length; i++) {
+ buffer.writeFloat(args[i]);
+ }
+ } else {
+ buffer.writeInt(0);
+ }
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int id = buffer.readInt();
+ int argLen = buffer.readInt();
+ float[] args = null;
+ if (argLen > 0) {
+ args = new float[argLen];
+ for (int i = 0; i < argLen; i++) {
+ args[i] = buffer.readFloat();
+ }
+ }
+
+ FloatFunctionCall data = new FloatFunctionCall(id, args);
+ operations.add(data);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Command to call the function")
+ .field(DocumentedOperation.INT, "id", "id of function to call")
+ .field(INT, "argLen", "the number of Arguments")
+ .field(FLOAT_ARRAY, "values", "argLen", "array of float arguments");
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {
+ RemoteContext remoteContext = context.getContext();
+ int[] args = mFunction.getArgs();
+ for (int j = 0; j < mOutArgs.length; j++) {
+ remoteContext.loadFloat(args[j], mOutArgs[j]);
+ updateVariables(remoteContext);
+ }
+ mFunction.execute(remoteContext);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java
new file mode 100644
index 000000000000..efd4eecb807e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatFunctionDefine.java
@@ -0,0 +1,168 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This defines a function Operator. It contains a collection of commands which are then executed by
+ * the FloatFunctionCall command
+ */
+public class FloatFunctionDefine extends Operation implements VariableSupport, Container {
+ private static final int OP_CODE = Operations.FUNCTION_DEFINE;
+ private static final String CLASS_NAME = "FunctionDefine";
+ private final int mId;
+ private final int[] mFloatVarId;
+ @NonNull private ArrayList<Operation> mList = new ArrayList<>();
+
+ @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+
+ /**
+ * @param id The id of the function
+ * @param floatVarId the ids of the variables
+ */
+ public FloatFunctionDefine(int id, int[] floatVarId) {
+ mId = id;
+ mFloatVarId = floatVarId;
+ }
+
+ @NonNull
+ @Override
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {}
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ context.putObject(mId, this);
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mId, mFloatVarId);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ String str = "FloatFunctionDefine[" + Utils.idString(mId) + "] (";
+ for (int j = 0; j < mFloatVarId.length; j++) {
+ str += "[" + mFloatVarId[j] + "] ";
+ }
+ str += ")";
+ for (Operation operation : mList) {
+ str += " \n " + operation.toString();
+ }
+ return str;
+ }
+
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the function
+ * @param varId the ids of the variables
+ */
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull int[] varId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(varId.length);
+ for (int i = 0; i < varId.length; i++) {
+ buffer.writeInt(varId[i]);
+ }
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int id = buffer.readInt();
+ int varLen = buffer.readInt();
+ int[] varId = new int[varLen];
+ for (int i = 0; i < varId.length; i++) {
+ varId[i] = buffer.readInt();
+ }
+ FloatFunctionDefine data = new FloatFunctionDefine(id, varId);
+ operations.add(data);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
+ .description("Define a function")
+ .field(DocumentedOperation.INT, "id", "The reference of the function")
+ .field(INT, "varLen", "number of arguments to the function")
+ .field(FLOAT_ARRAY, "id", "varLen", "id equations");
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ /**
+ * @return the array of id's
+ */
+ public int[] getArgs() {
+ return mFloatVarId;
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {}
+
+ /**
+ * Execute the function by applying the list of operations
+ *
+ * @param context the current RemoteContext
+ */
+ public void execute(@NonNull RemoteContext context) {
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context);
+ }
+
+ context.incrementOpCount();
+ op.apply(context);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
index 9e891c48c065..ee9e7a4045cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
@@ -39,7 +39,7 @@ import java.util.List;
* for constructing the particles
*/
public class ParticlesCreate extends Operation implements VariableSupport {
- private static final int OP_CODE = Operations.PARTICLE_CREATE;
+ private static final int OP_CODE = Operations.PARTICLE_DEFINE;
private static final String CLASS_NAME = "ParticlesCreate";
private final int mId;
private final float[][] mEquations;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
index 791079070622..8d19c94df604 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesLoop.java
@@ -38,7 +38,7 @@ import java.util.ArrayList;
import java.util.List;
/**
- * This provides the mechinism to evolve the particles It consist of a restart equation and a list
+ * This provides the mechanism to evolve the particles It consist of a restart equation and a list
* of equations particle restarts if restart equation > 0
*/
public class ParticlesLoop extends PaintOperation implements VariableSupport, Container {
@@ -159,10 +159,10 @@ public class ParticlesLoop extends PaintOperation implements VariableSupport, Co
/**
* Write the operation on the buffer
*
- * @param buffer
- * @param id
- * @param restart
- * @param equations
+ * @param buffer the buffer to write to
+ * @param id the id of the particle system
+ * @param restart the restart equation
+ * @param equations the equations to evolve the particles
*/
public static void apply(
@NonNull WireBuffer buffer,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index bd68d5a8c180..5f505409e254 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -289,4 +289,21 @@ public class Utils {
}
return 0;
}
+
+ /**
+ * Convert float alpha, red,g reen, blue to ARGB int
+ *
+ * @param alpha alpha value
+ * @param red red value
+ * @param green green value
+ * @param blue blue value
+ * @return ARGB int
+ */
+ public static int toARGB(float alpha, float red, float green, float blue) {
+ int a = (int) (alpha * 255.0f + 0.5f);
+ int r = (int) (red * 255.0f + 0.5f);
+ int g = (int) (green * 255.0f + 0.5f);
+ int b = (int) (blue * 255.0f + 0.5f);
+ return (a << 24 | r << 16 | g << 8 | b);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index 8e733ce1d808..96a31aec7dc4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -37,13 +37,15 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
/** Generic Component class */
public class Component extends PaintOperation
- implements Container, Measurable, SerializableToString {
+ implements Container, Measurable, SerializableToString, Serializable {
private static final boolean DEBUG = false;
@@ -61,7 +63,7 @@ public class Component extends PaintOperation
public boolean mNeedsMeasure = true;
public boolean mNeedsRepaint = false;
@Nullable public AnimateMeasure mAnimateMeasure;
- @NonNull public AnimationSpec mAnimationSpec = new AnimationSpec();
+ @NonNull public AnimationSpec mAnimationSpec = AnimationSpec.DEFAULT;
public boolean mFirstLayout = true;
@NonNull PaintBundle mPaint = new PaintBundle();
@NonNull protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
@@ -318,6 +320,14 @@ public class Component extends PaintOperation
}
}
+ protected AnimationSpec getAnimationSpec() {
+ return mAnimationSpec;
+ }
+
+ protected void setAnimationSpec(@NonNull AnimationSpec animationSpec) {
+ mAnimationSpec = animationSpec;
+ }
+
public enum Visibility {
GONE,
VISIBLE,
@@ -501,16 +511,17 @@ public class Component extends PaintOperation
*
* @param context
* @param document
- * @param x
- * @param y
+ * @param x x location on screen or -1 if unconditional click
+ * @param y y location on screen or -1 if unconditional click
*/
public void onClick(
@NonNull RemoteContext context, @NonNull CoreDocument document, float x, float y) {
- if (!contains(x, y)) {
+ boolean isUnconditional = x == -1 & y == -1;
+ if (!isUnconditional && !contains(x, y)) {
return;
}
- float cx = x - getScrollX();
- float cy = y - getScrollY();
+ float cx = isUnconditional ? -1 : x - getScrollX();
+ float cy = isUnconditional ? -1 : y - getScrollY();
for (Operation op : mList) {
if (op instanceof Component) {
((Component) op).onClick(context, document, cx, cy);
@@ -1035,4 +1046,15 @@ public class Component extends PaintOperation
}
return null;
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ serializer.add("type", getSerializedName());
+ serializer.add("id", mComponentId);
+ serializer.add("x", mX);
+ serializer.add("y", mY);
+ serializer.add("width", mWidth);
+ serializer.add("height", mHeight);
+ serializer.add("visibility", mVisibility);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index dcd334822010..c517e50f35d3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate
import com.android.internal.widget.remotecompose.core.operations.PaintData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
+import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
@@ -44,6 +45,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.ArrayList;
@@ -234,6 +236,8 @@ public class LayoutComponent extends Component {
mZIndexModifier = (ZIndexModifierOperation) op;
} else if (op instanceof GraphicsLayerModifierOperation) {
mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op;
+ } else if (op instanceof AnimationSpec) {
+ mAnimationSpec = (AnimationSpec) op;
} else if (op instanceof ScrollDelegate) {
ScrollDelegate scrollDelegate = (ScrollDelegate) op;
if (scrollDelegate.handlesHorizontalScroll()) {
@@ -256,6 +260,16 @@ public class LayoutComponent extends Component {
if (heightInConstraints != null) {
mHeightModifier.setHeightIn(heightInConstraints);
}
+
+ if (mAnimationSpec != AnimationSpec.DEFAULT) {
+ for (int i = 0; i < mChildrenComponents.size(); i++) {
+ Component c = mChildrenComponents.get(i);
+ if (c != null && c.getAnimationSpec() == AnimationSpec.DEFAULT) {
+ c.setAnimationSpec(mAnimationSpec);
+ }
+ }
+ }
+
setWidth(computeModifierDefinedWidth(null));
setHeight(computeModifierDefinedHeight(null));
}
@@ -473,4 +487,14 @@ public class LayoutComponent extends Component {
public ArrayList<Component> getChildrenComponents() {
return mChildrenComponents;
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("children", mChildrenComponents);
+ serializer.add("paddingLeft", mPaddingLeft);
+ serializer.add("paddingRight", mPaddingRight);
+ serializer.add("paddingTop", mPaddingTop);
+ serializer.add("paddingBottom", mPaddingBottom);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
index 4977a15e2dc1..a4e8f5c5f18e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
@@ -94,6 +94,11 @@ public class TouchCancelModifierOperation extends ListActionsOperation implement
return "TouchCancelModifier";
}
+ /**
+ * Write the operation on the buffer
+ *
+ * @param buffer a WireBuffer
+ */
public static void apply(WireBuffer buffer) {
buffer.start(OP_CODE);
}
@@ -108,6 +113,11 @@ public class TouchCancelModifierOperation extends ListActionsOperation implement
operations.add(new TouchCancelModifierOperation());
}
+ /**
+ * Add documentation for this operation
+ *
+ * @param doc a DocumentationBuilder
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, name())
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
index 8c51f2eac383..6191bf4e4ad2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
@@ -96,14 +96,30 @@ public class TouchDownModifierOperation extends ListActionsOperation implements
return "TouchModifier";
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ */
public static void apply(WireBuffer buffer) {
buffer.start(OP_CODE);
}
+ /**
+ * Read the operation from the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param operations the list of operations we read so far
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new TouchDownModifierOperation());
}
+ /**
+ * Add documentation for this operation
+ *
+ * @param doc a DocumentationBuilder
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, name())
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
index a12c356f7c48..a7e423e67940 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
@@ -94,14 +94,30 @@ public class TouchUpModifierOperation extends ListActionsOperation implements To
return "TouchUpModifier";
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ */
public static void apply(WireBuffer buffer) {
buffer.start(OP_CODE);
}
+ /**
+ * Read the operation from the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param operations the list of operations we read so far
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new TouchUpModifierOperation());
}
+ /**
+ * Add documentation for this operation
+ *
+ * @param doc a DocumentationBuilder
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, name())
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index d3b3e0e775f2..e5cd485967e8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -38,8 +38,8 @@ public class AnimateMeasure {
private final @NonNull Component mComponent;
private final @NonNull ComponentMeasure mOriginal;
private final @NonNull ComponentMeasure mTarget;
- private int mDuration;
- private int mDurationVisibilityChange = mDuration;
+ private float mDuration;
+ private float mDurationVisibilityChange = mDuration;
private @NonNull AnimationSpec.ANIMATION mEnterAnimation = AnimationSpec.ANIMATION.FADE_IN;
private @NonNull AnimationSpec.ANIMATION mExitAnimation = AnimationSpec.ANIMATION.FADE_OUT;
private int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
@@ -64,8 +64,8 @@ public class AnimateMeasure {
@NonNull Component component,
@NonNull ComponentMeasure original,
@NonNull ComponentMeasure target,
- int duration,
- int durationVisibilityChange,
+ float duration,
+ float durationVisibilityChange,
@NonNull AnimationSpec.ANIMATION enterAnimation,
@NonNull AnimationSpec.ANIMATION exitAnimation,
int motionEasingType,
@@ -94,6 +94,11 @@ public class AnimateMeasure {
component.mVisibility = target.getVisibility();
}
+ /**
+ * Update the current bounds/visibility/etc given the current time
+ *
+ * @param currentTime the time we use to evaluate the animation
+ */
public void update(long currentTime) {
long elapsed = currentTime - mStartTime;
float motionProgress = elapsed / (float) mDuration;
@@ -347,6 +352,11 @@ public class AnimateMeasure {
return mOriginal.getH() * (1 - mP) + mTarget.getH() * mP;
}
+ /**
+ * Returns the visibility for this measure
+ *
+ * @return the current visibility (possibly interpolated)
+ */
public float getVisibility() {
if (mOriginal.getVisibility() == mTarget.getVisibility()) {
return 1f;
@@ -357,6 +367,12 @@ public class AnimateMeasure {
}
}
+ /**
+ * Set the target values from the given measure
+ *
+ * @param measure the target measure
+ * @param currentTime the current time
+ */
public void updateTarget(@NonNull ComponentMeasure measure, long currentTime) {
mOriginal.setX(getX());
mOriginal.setY(getY());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 6dff4a87088b..6e9de58e354a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -24,25 +24,28 @@ import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.GeneralEasing;
import java.util.List;
/** Basic component animation spec */
-public class AnimationSpec extends Operation {
+public class AnimationSpec extends Operation implements ModifierOperation {
+ public static final AnimationSpec DEFAULT = new AnimationSpec();
int mAnimationId = -1;
- int mMotionDuration = 300;
+ float mMotionDuration = 300;
int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
- int mVisibilityDuration = 300;
+ float mVisibilityDuration = 300;
int mVisibilityEasingType = GeneralEasing.CUBIC_STANDARD;
@NonNull ANIMATION mEnterAnimation = ANIMATION.FADE_IN;
@NonNull ANIMATION mExitAnimation = ANIMATION.FADE_OUT;
public AnimationSpec(
int animationId,
- int motionDuration,
+ float motionDuration,
int motionEasingType,
- int visibilityDuration,
+ float visibilityDuration,
int visibilityEasingType,
@NonNull ANIMATION enterAnimation,
@NonNull ANIMATION exitAnimation) {
@@ -70,7 +73,7 @@ public class AnimationSpec extends Operation {
return mAnimationId;
}
- public int getMotionDuration() {
+ public float getMotionDuration() {
return mMotionDuration;
}
@@ -78,7 +81,7 @@ public class AnimationSpec extends Operation {
return mMotionEasingType;
}
- public int getVisibilityDuration() {
+ public float getVisibilityDuration() {
return mVisibilityDuration;
}
@@ -102,6 +105,25 @@ public class AnimationSpec extends Operation {
return "ANIMATION_SPEC (" + mMotionDuration + " ms)";
}
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(
+ indent,
+ "ANIMATION_SPEC = ["
+ + getMotionDuration()
+ + ", "
+ + getMotionEasingType()
+ + ", "
+ + getVisibilityDuration()
+ + ", "
+ + getVisibilityEasingType()
+ + ", "
+ + getEnterAnimation()
+ + ", "
+ + getExitAnimation()
+ + "]");
+ }
+
public enum ANIMATION {
FADE_IN,
FADE_OUT,
@@ -156,10 +178,22 @@ public class AnimationSpec extends Operation {
return Operations.ANIMATION_SPEC;
}
+ /**
+ * Returns an int for the given ANIMATION
+ *
+ * @param animation an ANIMATION enum value
+ * @return a corresponding int value
+ */
public static int animationToInt(@NonNull ANIMATION animation) {
return animation.ordinal();
}
+ /**
+ * Maps int value to the corresponding ANIMATION enum values
+ *
+ * @param value int value mapped to the enum
+ * @return the corresponding ANIMATION enum value
+ */
@NonNull
public static ANIMATION intToAnimation(int value) {
switch (value) {
@@ -184,20 +218,32 @@ public class AnimationSpec extends Operation {
}
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param animationId the animation id
+ * @param motionDuration the duration of the motion animation
+ * @param motionEasingType the type of easing for the motion animation
+ * @param visibilityDuration the duration of the visibility animation
+ * @param visibilityEasingType the type of easing for the visibility animation
+ * @param enterAnimation the type of animation when "entering" (newly visible)
+ * @param exitAnimation the type of animation when "exiting" (newly gone)
+ */
public static void apply(
@NonNull WireBuffer buffer,
int animationId,
- int motionDuration,
+ float motionDuration,
int motionEasingType,
- int visibilityDuration,
+ float visibilityDuration,
int visibilityEasingType,
@NonNull ANIMATION enterAnimation,
@NonNull ANIMATION exitAnimation) {
buffer.start(Operations.ANIMATION_SPEC);
buffer.writeInt(animationId);
- buffer.writeInt(motionDuration);
+ buffer.writeFloat(motionDuration);
buffer.writeInt(motionEasingType);
- buffer.writeInt(visibilityDuration);
+ buffer.writeFloat(visibilityDuration);
buffer.writeInt(visibilityEasingType);
buffer.writeInt(animationToInt(enterAnimation));
buffer.writeInt(animationToInt(exitAnimation));
@@ -211,9 +257,9 @@ public class AnimationSpec extends Operation {
*/
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int animationId = buffer.readInt();
- int motionDuration = buffer.readInt();
+ float motionDuration = buffer.readFloat();
int motionEasingType = buffer.readInt();
- int visibilityDuration = buffer.readInt();
+ float visibilityDuration = buffer.readFloat();
int visibilityEasingType = buffer.readInt();
ANIMATION enterAnimation = intToAnimation(buffer.readInt());
ANIMATION exitAnimation = intToAnimation(buffer.readInt());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 64e2f004cb65..051579b02cee 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -30,6 +30,15 @@ public class ParticleAnimation {
@NonNull PaintBundle mPaint = new PaintBundle();
+ /**
+ * Animate the particle animation
+ *
+ * @param context the current paint context
+ * @param component the target component
+ * @param start the component's measure at the end of the animation
+ * @param end the component's measure at the end of the animation
+ * @param progress the current animation progress
+ */
public void animate(
@NonNull PaintContext context,
@NonNull Component component,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index a37f35f0c8d8..35d639e65385 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -29,6 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.List;
@@ -191,6 +192,15 @@ public class BoxLayout extends LayoutManager {
return Operations.LAYOUT_BOX;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param componentId the component id
+ * @param animationId the component animation id
+ * @param horizontalPositioning the horizontal positioning rules
+ * @param verticalPositioning the vertical positioning rules
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -260,4 +270,28 @@ public class BoxLayout extends LayoutManager {
public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+ serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+ }
+
+ private String getPositioningString(int pos) {
+ switch (pos) {
+ case START:
+ return "START";
+ case CENTER:
+ return "CENTER";
+ case END:
+ return "END";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ default:
+ return "NONE";
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index 0091a47eebfb..8448132cbcc1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -28,6 +28,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.List;
@@ -91,6 +92,13 @@ public class CanvasLayout extends BoxLayout {
return Operations.LAYOUT_CANVAS;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param componentId the component id
+ * @param animationId the component animation id
+ */
public static void apply(@NonNull WireBuffer buffer, int componentId, int animationId) {
buffer.start(Operations.LAYOUT_CANVAS);
buffer.writeInt(componentId);
@@ -142,4 +150,27 @@ public class CanvasLayout extends BoxLayout {
public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId, mAnimationId);
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("", mHorizontalPositioning);
+ }
+
+ private String getPositioningString(int pos) {
+ switch (pos) {
+ case START:
+ return "START";
+ case CENTER:
+ return "CENTER";
+ case END:
+ return "END";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ default:
+ return "NONE";
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 4d0cbefb0c92..47a55b6ed82a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -34,6 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.List;
@@ -218,6 +219,8 @@ public class ColumnLayout extends LayoutManager {
boolean checkWeights = true;
while (checkWeights) {
checkWeights = false;
+ childrenWidth = 0f;
+ childrenHeight = 0f;
boolean hasWeights = false;
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
@@ -477,4 +480,35 @@ public class ColumnLayout extends LayoutManager {
mVerticalPositioning,
mSpacedBy);
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+ serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+ serializer.add("spacedBy", mSpacedBy);
+ }
+
+ private String getPositioningString(int pos) {
+ switch (pos) {
+ case START:
+ return "START";
+ case CENTER:
+ return "CENTER";
+ case END:
+ return "END";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ case SPACE_BETWEEN:
+ return "SPACE_BETWEEN";
+ case SPACE_EVENLY:
+ return "SPACE_EVENLY";
+ case SPACE_AROUND:
+ return "SPACE_AROUND";
+ default:
+ return "NONE";
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 5b35c4c70702..e93cbd74b0b5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -34,6 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.List;
@@ -218,6 +219,8 @@ public class RowLayout extends LayoutManager {
while (checkWeights) {
checkWeights = false;
+ childrenWidth = 0f;
+ childrenHeight = 0f;
boolean hasWeights = false;
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
@@ -481,4 +484,35 @@ public class RowLayout extends LayoutManager {
mVerticalPositioning,
mSpacedBy);
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("verticalPositioning", getPositioningString(mVerticalPositioning));
+ serializer.add("horizontalPositioning", getPositioningString(mHorizontalPositioning));
+ serializer.add("spacedBy", mSpacedBy);
+ }
+
+ private String getPositioningString(int pos) {
+ switch (pos) {
+ case START:
+ return "START";
+ case CENTER:
+ return "CENTER";
+ case END:
+ return "END";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ case SPACE_BETWEEN:
+ return "SPACE_BETWEEN";
+ case SPACE_EVENLY:
+ return "SPACE_EVENLY";
+ case SPACE_AROUND:
+ return "SPACE_AROUND";
+ default:
+ return "NONE";
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index 3044797b17c9..ee16bc2f4459 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -30,6 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -81,6 +82,7 @@ public class StateLayout extends LayoutManager {
hideLayoutsOtherThan(currentLayoutIndex);
}
+ /** Traverse the list of children and identify animated components across states */
public void findAnimatedComponents() {
for (int i = 0; i < mChildrenComponents.size(); i++) {
Component cs = mChildrenComponents.get(i);
@@ -105,6 +107,10 @@ public class StateLayout extends LayoutManager {
collapsePaintedComponents();
}
+ /**
+ * Traverse the list of components in different states, and if they are similar pick the first
+ * component for painting in all states.
+ */
public void collapsePaintedComponents() {
int numStates = mChildrenComponents.size();
for (Integer id : statePaintedComponents.keySet()) {
@@ -346,6 +352,11 @@ public class StateLayout extends LayoutManager {
measuredLayoutIndex = currentLayoutIndex;
}
+ /**
+ * Hides all layouts that are not the one with the given id
+ *
+ * @param idx the layout id
+ */
public void hideLayoutsOtherThan(int idx) {
int index = 0;
for (Component pane : mChildrenComponents) {
@@ -360,6 +371,12 @@ public class StateLayout extends LayoutManager {
}
}
+ /**
+ * Returns the layout with the given id
+ *
+ * @param idx the component id
+ * @return the LayoutManager with the given id, or the first child of StateLayout if not found
+ */
public @NonNull LayoutManager getLayout(int idx) {
int index = 0;
for (Component pane : mChildrenComponents) {
@@ -485,6 +502,7 @@ public class StateLayout extends LayoutManager {
}
}
+ /** Check if we are at the end of the transition, and if so handles it. */
public void checkEndOfTransition() {
LayoutManager currentLayout = getLayout(measuredLayoutIndex);
LayoutManager previousLayout = getLayout(previousLayoutIndex);
@@ -536,10 +554,16 @@ public class StateLayout extends LayoutManager {
return "STATE_LAYOUT";
}
- // companion object {
- // fun documentation(doc: OrigamiDocumentation) {}
- // }
-
+ /**
+ * write the operation to the buffer
+ *
+ * @param buffer the current buffer
+ * @param componentId the component id
+ * @param animationId the animation id if there's one, -1 otherwise.
+ * @param horizontalPositioning the horizontal positioning rule
+ * @param verticalPositioning the vertical positioning rule
+ * @param indexId the current index
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -570,4 +594,10 @@ public class StateLayout extends LayoutManager {
operations.add(
new StateLayout(null, componentId, animationId, 0f, 0f, 100f, 100f, indexId));
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("indexId", mIndexId);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 8157ea05ec45..e8e95db8141d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -34,6 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import java.util.List;
@@ -331,6 +332,20 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
return Operations.LAYOUT_TEXT;
}
+ /**
+ * Write the operation in the buffer
+ *
+ * @param buffer the WireBuffer we write on
+ * @param componentId the component id
+ * @param animationId the animation id (-1 if not set)
+ * @param textId the text id
+ * @param color the text color
+ * @param fontSize the font size
+ * @param fontStyle the font style
+ * @param fontWeight the font weight
+ * @param fontFamilyId the font family id
+ * @param textAlign the alignment rules
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -418,4 +433,16 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
mFontFamilyId,
mTextAlign);
}
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ super.serialize(serializer);
+ serializer.add("textId", mTextId);
+ serializer.add("color", mColor);
+ serializer.add("fontSize", mFontSize);
+ serializer.add("fontStyle", mFontStyle);
+ serializer.add("fontWeight", mFontWeight);
+ serializer.add("fontFamilyId", mFontFamilyId);
+ serializer.add("textAlign", mTextAlign);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 82f23cdcf766..11ed9f435070 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -92,6 +92,11 @@ public class ComponentMeasure {
component.mVisibility);
}
+ /**
+ * Initialize this ComponentMeasure from another ComponentMeasure instance.
+ *
+ * @param m the ComponentMeasure to copy from
+ */
public void copyFrom(@NonNull ComponentMeasure m) {
mX = m.mX;
mY = m.mY;
@@ -100,6 +105,12 @@ public class ComponentMeasure {
mVisibility = m.mVisibility;
}
+ /**
+ * Returns true if the ComponentMeasure passed is identical to us
+ *
+ * @param m the ComponentMeasure to check
+ * @return true if the passed ComponentMeasure is identical to ourself
+ */
public boolean same(@NonNull ComponentMeasure m) {
return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 5cfb1b43cf15..b14f2d9f8a94 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -28,10 +28,17 @@ import java.util.HashMap;
public class MeasurePass {
@NonNull HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
+ /** Clear the MeasurePass */
public void clear() {
mList.clear();
}
+ /**
+ * Add a ComponentMeasure to the MeasurePass
+ *
+ * @param measure the ComponentMeasure to add
+ * @throws Exception
+ */
public void add(@NonNull ComponentMeasure measure) throws Exception {
if (measure.mId == -1) {
throw new Exception("Component has no id!");
@@ -39,10 +46,22 @@ public class MeasurePass {
mList.put(measure.mId, measure);
}
+ /**
+ * Returns true if the current MeasurePass already contains a ComponentMeasure for the given id.
+ *
+ * @param id
+ * @return
+ */
public boolean contains(int id) {
return mList.containsKey(id);
}
+ /**
+ * return the ComponentMeasure associated with a given component
+ *
+ * @param c the Component
+ * @return the associated ComponentMeasure
+ */
public @NonNull ComponentMeasure get(@NonNull Component c) {
if (!mList.containsKey(c.getComponentId())) {
ComponentMeasure measure =
@@ -54,6 +73,12 @@ public class MeasurePass {
return mList.get(c.getComponentId());
}
+ /**
+ * Returns the ComponentMeasure associated with the id, creating one if none exists.
+ *
+ * @param id the component id
+ * @return the associated ComponentMeasure
+ */
public @NonNull ComponentMeasure get(int id) {
if (!mList.containsKey(id)) {
ComponentMeasure measure =
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index b4240d0e08a7..ac23db0ed599 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -130,6 +130,20 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer the WireBuffer
+ * @param x x coordinate of the background rect
+ * @param y y coordinate of the background rect
+ * @param width width of the background rect
+ * @param height height of the background rect
+ * @param r red component of the background color
+ * @param g green component of the background color
+ * @param b blue component of the background color
+ * @param a alpha component of the background color
+ * @param shapeType the shape of the background (RECTANGLE=0, CIRCLE=1)
+ */
public static void apply(
@NonNull WireBuffer buffer,
float x,
@@ -205,6 +219,6 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
.field(FLOAT, "g", "")
.field(FLOAT, "b", "")
.field(FLOAT, "a", "")
- .field(FLOAT, "shapeType", "");
+ .field(FLOAT, "shapeType", "0 for RECTANGLE, 1 for CIRCLE");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index df30d9f615e5..06c21bd49f33 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -176,6 +176,22 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer the WireBuffer
+ * @param x x coordinate of the border rect
+ * @param y y coordinate of the border rect
+ * @param width width of the border rect
+ * @param height height of the border rect
+ * @param borderWidth the width of the border outline
+ * @param roundedCorner rounded corner value in pixels
+ * @param r red component of the border color
+ * @param g green component of the border color
+ * @param b blue component of the border color
+ * @param a alpha component of the border color
+ * @param shapeType the shape type (0 = RECTANGLE, 1 = CIRCLE)
+ */
public static void apply(
@NonNull WireBuffer buffer,
float x,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index b27fb9200398..ce4449355434 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -76,6 +76,11 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer the WireBuffer
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index a1609ace2138..dd27f8b6cfe6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -21,6 +21,7 @@ import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
@@ -36,7 +37,7 @@ import java.util.ArrayList;
/** Maintain a list of modifiers */
public class ComponentModifiers extends PaintOperation
- implements DecoratorComponent, ClickHandler, TouchHandler {
+ implements DecoratorComponent, ClickHandler, TouchHandler, SerializableToString {
@NonNull ArrayList<ModifierOperation> mList = new ArrayList<>();
@NonNull
@@ -68,6 +69,7 @@ public class ComponentModifiers extends PaintOperation
// nothing
}
+ @Override
public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, "MODIFIERS");
for (ModifierOperation m : mList) {
@@ -75,10 +77,20 @@ public class ComponentModifiers extends PaintOperation
}
}
+ /**
+ * Add a ModifierOperation
+ *
+ * @param operation a ModifierOperation
+ */
public void add(@NonNull ModifierOperation operation) {
mList.add(operation);
}
+ /**
+ * Returns the size of the modifier list
+ *
+ * @return number of modifiers
+ */
public int size() {
return mList.size();
}
@@ -133,6 +145,11 @@ public class ComponentModifiers extends PaintOperation
}
}
+ /**
+ * Add the operations to this ComponentModifier
+ *
+ * @param operations list of ModifierOperation
+ */
public void addAll(@NonNull ArrayList<ModifierOperation> operations) {
mList.addAll(operations);
}
@@ -197,6 +214,11 @@ public class ComponentModifiers extends PaintOperation
}
}
+ /**
+ * Returns true if we have a horizontal scroll modifier
+ *
+ * @return true if we have a horizontal scroll modifier, false otherwise
+ */
public boolean hasHorizontalScroll() {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
@@ -209,6 +231,11 @@ public class ComponentModifiers extends PaintOperation
return false;
}
+ /**
+ * Returns true if we have a vertical scroll modifier
+ *
+ * @return true if we have a vertical scroll modifier, false otherwise
+ */
public boolean hasVerticalScroll() {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
@@ -221,6 +248,12 @@ public class ComponentModifiers extends PaintOperation
return false;
}
+ /**
+ * Set the horizontal scroll dimension (if we have a scroll modifier)
+ *
+ * @param hostDimension the host component horizontal dimension
+ * @param contentDimension the content horizontal dimension
+ */
public void setHorizontalScrollDimension(float hostDimension, float contentDimension) {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
@@ -232,6 +265,12 @@ public class ComponentModifiers extends PaintOperation
}
}
+ /**
+ * Set the vertical scroll dimension (if we have a scroll modifier)
+ *
+ * @param hostDimension the host component vertical dimension
+ * @param contentDimension the content vertical dimension
+ */
public void setVerticalScrollDimension(float hostDimension, float contentDimension) {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
@@ -243,6 +282,11 @@ public class ComponentModifiers extends PaintOperation
}
}
+ /**
+ * Returns the horizontal scroll dimension if we have a scroll modifier
+ *
+ * @return the horizontal scroll dimension, or 0 if no scroll modifier
+ */
public float getHorizontalScrollDimension() {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
@@ -255,6 +299,11 @@ public class ComponentModifiers extends PaintOperation
return 0f;
}
+ /**
+ * Returns the vertical scroll dimension if we have a scroll modifier
+ *
+ * @return the vertical scroll dimension, or 0 if no scroll modifier
+ */
public float getVerticalScrollDimension() {
for (ModifierOperation op : mList) {
if (op instanceof ScrollModifierOperation) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index c377b756ff38..dd22391c43ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -52,6 +52,11 @@ public class ComponentVisibilityOperation extends Operation
return "ComponentVisibilityOperation(" + mVisibilityId + ")";
}
+ /**
+ * Returns the serialized name for this operation
+ *
+ * @return the serialized name
+ */
@NonNull
public String serializedName() {
return "COMPONENT_VISIBILITY";
@@ -74,6 +79,12 @@ public class ComponentVisibilityOperation extends Operation
@Override
public void write(@NonNull WireBuffer buffer) {}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId visibility value
+ */
public static void apply(@NonNull WireBuffer buffer, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java
new file mode 100644
index 000000000000..7c9acfe8d2e6
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionInModifierOperation.java
@@ -0,0 +1,109 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+/** Helper class to set the min / max dimension on a component */
+public class DimensionInModifierOperation extends Operation
+ implements ModifierOperation, VariableSupport {
+ int mOpCode = -1;
+
+ float mV1;
+ float mV2;
+ float mValue1;
+ float mValue2;
+
+ public DimensionInModifierOperation(int opcode, float min, float max) {
+ mOpCode = opcode;
+ mValue1 = min;
+ mValue2 = max;
+ if (!Float.isNaN(mValue1)) {
+ mV1 = mValue1;
+ }
+ if (!Float.isNaN(mValue2)) {
+ mV2 = mValue2;
+ }
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ if (mV1 != -1) {
+ mV1 = mV1 * context.getDensity();
+ }
+ if (mV2 != -1) {
+ mV2 = mV2 * context.getDensity();
+ }
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ // nothing
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ // nothing
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+
+ /**
+ * Returns the min value
+ *
+ * @return minimum value
+ */
+ public float getMin() {
+ return mV1;
+ }
+
+ /**
+ * Returns the max value
+ *
+ * @return maximum value
+ */
+ public float getMax() {
+ return mV2;
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "WIDTH_IN = [" + getMin() + ", " + getMax() + "]");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index b11deae3d196..88449c4a9016 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -104,6 +104,11 @@ public abstract class DimensionModifierOperation extends Operation
}
}
+ /**
+ * Returns true if the dimension is set using a weight
+ *
+ * @return true if using weight, false otherwise
+ */
public boolean hasWeight() {
return mType == Type.WEIGHT;
}
@@ -136,6 +141,11 @@ public abstract class DimensionModifierOperation extends Operation
mOutValue = mValue = value;
}
+ /**
+ * Returns the serialized name for this operation
+ *
+ * @return the serialized name
+ */
@NonNull
public String serializedName() {
return "DIMENSION";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
index 15c2f46093d2..dc5918037946 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
@@ -222,6 +222,26 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param scaleX scaleX of the layer
+ * @param scaleY scaleY of the layer
+ * @param rotationX rotationX of the layer
+ * @param rotationY rotationY of the layer
+ * @param rotationZ rotationZ of the layer
+ * @param shadowElevation the shadow elevation
+ * @param transformOriginX the X origin of the transformations
+ * @param transformOriginY the Y origin of the transformations
+ * @param alpha the alpha of the layer
+ * @param cameraDistance the camera distance
+ * @param blendMode blending mode of the layer
+ * @param spotShadowColorId the spot shadow color id
+ * @param ambientShadowColorId the ambient shadow color id
+ * @param colorFilterId the color filter id
+ * @param renderEffectId the render effect id
+ */
public static void apply(
WireBuffer buffer,
float scaleX,
@@ -257,6 +277,12 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation {
buffer.writeInt(renderEffectId);
}
+ /**
+ * Read the operation from the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param operations the list of operations read so far
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
float scaleX = buffer.readFloat();
float scaleY = buffer.readFloat();
@@ -292,6 +318,11 @@ public class GraphicsLayerModifierOperation extends DecoratorModifierOperation {
renderEffectId));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the GraphicsLayer Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
index c19bd2f6b7c0..cc32f2699dfe 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
@@ -19,47 +19,37 @@ import android.annotation.NonNull;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
-import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
/** Set the min / max height dimension on a component */
-public class HeightInModifierOperation extends DrawBase2 implements ModifierOperation {
+public class HeightInModifierOperation extends DimensionInModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT_IN;
public static final String CLASS_NAME = "HeightInModifierOperation";
- /**
- * Read this operation and add it to the list of operations
- *
- * @param buffer the buffer to read
- * @param operations the list of operations that will be added to
- */
- public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
- Maker m = HeightInModifierOperation::new;
- read(m, buffer, operations);
+ public HeightInModifierOperation(float min, float max) {
+ super(OP_CODE, min, max);
}
- /**
- * Returns the min value
- *
- * @return minimum value
- */
- public float getMin() {
- return mV1;
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, getMin(), getMax());
}
/**
- * Returns the max value
+ * Read this operation and add it to the list of operations
*
- * @return maximum value
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
*/
- public float getMax() {
- return mV2;
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+ operations.add(new HeightInModifierOperation(v1, v2));
}
/**
@@ -81,11 +71,6 @@ public class HeightInModifierOperation extends DrawBase2 implements ModifierOper
return CLASS_NAME;
}
- @Override
- protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
- apply(buffer, v1, v2);
- }
-
/**
* Populate the documentation with a description of this operation
*
@@ -98,14 +83,6 @@ public class HeightInModifierOperation extends DrawBase2 implements ModifierOper
.field(DocumentedOperation.FLOAT, "max", "The maximum height, -1 if not applied");
}
- public HeightInModifierOperation(float min, float max) {
- super(min, max);
- mName = CLASS_NAME;
- }
-
- @Override
- public void paint(@NonNull PaintContext context) {}
-
/**
* Writes out the HeightInModifier to the buffer
*
@@ -114,7 +91,9 @@ public class HeightInModifierOperation extends DrawBase2 implements ModifierOper
* @param y1 start y of the DrawOval
*/
public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
- write(buffer, OP_CODE, x1, y1);
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index 4b50a916b9cd..154740d5536c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -52,6 +52,13 @@ public class HeightModifierOperation extends DimensionModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param type the type of dimension rule (DimensionModifierOperation.Type)
+ * @param value the value of the dimension
+ */
public static void apply(@NonNull WireBuffer buffer, int type, float value) {
buffer.start(OP_CODE);
buffer.writeInt(type);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index 2e9d6619d011..09e2228b847a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -23,6 +23,7 @@ import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
@@ -32,7 +33,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
/** Capture a host action information. This can be triggered on eg. a click. */
-public class HostActionOperation extends Operation implements ActionOperation {
+public class HostActionOperation extends Operation
+ implements ActionOperation, SerializableToString {
private static final int OP_CODE = Operations.HOST_ACTION;
int mActionId = -1;
@@ -51,6 +53,11 @@ public class HostActionOperation extends Operation implements ActionOperation {
return mActionId;
}
+ /**
+ * Returns the serialized name for this operation
+ *
+ * @return the serialized name
+ */
@NonNull
public String serializedName() {
return "HOST_ACTION";
@@ -83,6 +90,12 @@ public class HostActionOperation extends Operation implements ActionOperation {
context.runAction(mActionId, "");
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param actionId the action id
+ */
public static void apply(@NonNull WireBuffer buffer, int actionId) {
buffer.start(OP_CODE);
buffer.writeInt(actionId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 49ef58e0fe53..8a8809c653f8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -57,6 +57,11 @@ public class HostNamedActionOperation extends Operation implements ActionOperati
return "HostNamedActionOperation(" + mTextId + " : " + mValueId + ")";
}
+ /**
+ * Name used during serialization
+ *
+ * @return the serialized name for this operation
+ */
@NonNull
public String serializedName() {
return "HOST_NAMED_ACTION";
@@ -105,6 +110,14 @@ public class HostNamedActionOperation extends Operation implements ActionOperati
context.runNamedAction(mTextId, value);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param textId the text id of the action
+ * @param type the type of the action
+ * @param valueId the value id associated with the action
+ */
public static void apply(@NonNull WireBuffer buffer, int textId, int type, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
index 9588e99a65b6..4ad11d267406 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/MarqueeModifierOperation.java
@@ -110,6 +110,12 @@ public class MarqueeModifierOperation extends DecoratorModifierOperation impleme
mVelocity);
}
+ /**
+ * Serialize the string
+ *
+ * @param indent padding to display
+ * @param serializer append the string
+ */
// @Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(indent, "MARQUEE = [" + mIterations + "]");
@@ -153,14 +159,35 @@ public class MarqueeModifierOperation extends DecoratorModifierOperation impleme
return "MarqueeModifierOperation(" + mIterations + ")";
}
+ /**
+ * Name of the operation
+ *
+ * @return name
+ */
public static String name() {
return CLASS_NAME;
}
+ /**
+ * id of the operation
+ *
+ * @return the operation id
+ */
public static int id() {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param iterations the number of iterations
+ * @param animationMode animation mode
+ * @param repeatDelayMillis repeat delay in ms
+ * @param initialDelayMillis initial delay before the marquee start in ms
+ * @param spacing the spacing between marquee
+ * @param velocity the velocity of the marquee animation
+ */
public static void apply(
WireBuffer buffer,
int iterations,
@@ -178,6 +205,12 @@ public class MarqueeModifierOperation extends DecoratorModifierOperation impleme
buffer.writeFloat(velocity);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
int iterations = buffer.readInt();
int animationMode = buffer.readInt();
@@ -195,6 +228,11 @@ public class MarqueeModifierOperation extends DecoratorModifierOperation impleme
velocity));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("specify a Marquee Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index f8926fef56fa..a86fb2c1f6a5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -22,5 +22,11 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
/** Represents a modifier */
public interface ModifierOperation extends OperationInterface {
+ /**
+ * Serialize the string
+ *
+ * @param indent padding to display
+ * @param serializer append the string
+ */
void serializeToString(int indent, @NonNull StringSerializer serializer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
index 42719478faf0..2cd2728f0720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
@@ -65,6 +65,12 @@ public class OffsetModifierOperation extends DecoratorModifierOperation {
apply(buffer, mX, mY);
}
+ /**
+ * Serialize the string
+ *
+ * @param indent padding to display
+ * @param serializer append the string
+ */
// @Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(indent, "OFFSET = [" + mX + ", " + mY + "]");
@@ -110,18 +116,36 @@ public class OffsetModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param x x offset
+ * @param y y offset
+ */
public static void apply(WireBuffer buffer, float x, float y) {
buffer.start(OP_CODE);
buffer.writeFloat(x);
buffer.writeFloat(y);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
float x = buffer.readFloat();
float y = buffer.readFloat();
operations.add(new OffsetModifierOperation(x, y));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Offset Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index bcfbdd68472f..3225d5c6f92c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -132,6 +132,15 @@ public class PaddingModifierOperation extends Operation implements ModifierOpera
return Operations.MODIFIER_PADDING;
}
+ /**
+ * Write operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param left left padding
+ * @param top top padding
+ * @param right right padding
+ * @param bottom bottom padding
+ */
public static void apply(
@NonNull WireBuffer buffer, float left, float top, float right, float bottom) {
buffer.start(Operations.MODIFIER_PADDING);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
index fe074e4754e2..9787d9b4b399 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RippleModifierOperation.java
@@ -143,19 +143,40 @@ public class RippleModifierOperation extends DecoratorModifierOperation implemen
serializer.append(indent, "RIPPLE_MODIFIER");
}
+ /**
+ * The operation name
+ *
+ * @return operation name
+ */
@NonNull
public static String name() {
return "RippleModifier";
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new RippleModifierOperation());
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, name())
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 8950579354b7..76b3373a52d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -135,6 +135,12 @@ public class ScrollModifierOperation extends ListActionsOperation
apply(buffer, mDirection, mPositionExpression, mMax, mNotchMax);
}
+ /**
+ * Serialize the string
+ *
+ * @param indent padding to display
+ * @param serializer append the string
+ */
// @Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(indent, "SCROLL = [" + mDirection + "]");
@@ -190,6 +196,15 @@ public class ScrollModifierOperation extends ListActionsOperation
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param direction direction of the scroll (HORIZONTAL, VERTICAL)
+ * @param position the current position
+ * @param max the maximum position
+ * @param notchMax the maximum notch
+ */
public static void apply(
WireBuffer buffer, int direction, float position, float max, float notchMax) {
buffer.start(OP_CODE);
@@ -199,6 +214,12 @@ public class ScrollModifierOperation extends ListActionsOperation
buffer.writeFloat(notchMax);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
int direction = buffer.readInt();
float position = buffer.readFloat();
@@ -207,6 +228,11 @@ public class ScrollModifierOperation extends ListActionsOperation
operations.add(new ScrollModifierOperation(direction, position, max, notchMax));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define a Scroll Modifier")
@@ -300,12 +326,24 @@ public class ScrollModifierOperation extends ListActionsOperation
public void onTouchCancel(
RemoteContext context, CoreDocument document, Component component, float x, float y) {}
+ /**
+ * Set the horizontal scroll dimension
+ *
+ * @param hostDimension the horizontal host dimension
+ * @param contentDimension the horizontal content dimension
+ */
public void setHorizontalScrollDimension(float hostDimension, float contentDimension) {
mHostDimension = hostDimension;
mContentDimension = contentDimension;
mMaxScrollX = contentDimension - hostDimension;
}
+ /**
+ * Set the vertical scroll dimension
+ *
+ * @param hostDimension the vertical host dimension
+ * @param contentDimension the vertical content dimension
+ */
public void setVerticalScrollDimension(float hostDimension, float contentDimension) {
mHostDimension = hostDimension;
mContentDimension = contentDimension;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
index b6977a035c9e..d625900fcf2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
@@ -49,6 +49,11 @@ public class ValueFloatChangeActionOperation extends Operation implements Action
return "ValueFloatChangeActionOperation(" + mTargetValueId + ")";
}
+ /**
+ * The name of the operation used during serialization
+ *
+ * @return the operation serialized name
+ */
public String serializedName() {
return "VALUE_FLOAT_CHANGE";
}
@@ -76,18 +81,36 @@ public class ValueFloatChangeActionOperation extends Operation implements Action
context.overrideFloat(mTargetValueId, mValue);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId the value id
+ * @param value the value to set
+ */
public static void apply(WireBuffer buffer, int valueId, float value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
buffer.writeFloat(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
int valueId = buffer.readInt();
float value = buffer.readFloat();
operations.add(new ValueFloatChangeActionOperation(valueId, value));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueFloatChangeActionOperation")
.description(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
index 766271a70ce4..3f26c5e5575b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatExpressionChangeActionOperation.java
@@ -50,6 +50,11 @@ public class ValueFloatExpressionChangeActionOperation extends Operation
return "ValueFloatExpressionChangeActionOperation(" + mTargetValueId + ")";
}
+ /**
+ * The name of the operation used during serialization
+ *
+ * @return the operation serialized name
+ */
@NonNull
public String serializedName() {
return "VALUE_FLOAT_EXPRESSION_CHANGE";
@@ -83,6 +88,13 @@ public class ValueFloatExpressionChangeActionOperation extends Operation
document.evaluateFloatExpression(mValueExpressionId, mTargetValueId, context);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId the value id
+ * @param value the value to set
+ */
public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 60166a7b2102..8c5bb6fdb268 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -49,6 +49,11 @@ public class ValueIntegerChangeActionOperation extends Operation implements Acti
return "ValueChangeActionOperation(" + mTargetValueId + ")";
}
+ /**
+ * The name of the operation used during serialization
+ *
+ * @return the operation serialized name
+ */
@NonNull
public String serializedName() {
return "VALUE_INTEGER_CHANGE";
@@ -81,6 +86,13 @@ public class ValueIntegerChangeActionOperation extends Operation implements Acti
context.overrideInteger(mTargetValueId, mValue);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId the value id
+ * @param value the value to set
+ */
public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
index 502508058465..00c80f12aaba 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -50,6 +50,11 @@ public class ValueIntegerExpressionChangeActionOperation extends Operation
return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
}
+ /**
+ * The name of the operation used during serialization
+ *
+ * @return the operation serialized name
+ */
@NonNull
public String serializedName() {
return "VALUE_INTEGER_EXPRESSION_CHANGE";
@@ -83,6 +88,13 @@ public class ValueIntegerExpressionChangeActionOperation extends Operation
document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId the long id pointing to an int value
+ * @param value the value to set (long id)`
+ */
public static void apply(@NonNull WireBuffer buffer, long valueId, long value) {
buffer.start(OP_CODE);
buffer.writeLong(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index 8093bb3c64ec..57e30d4126ba 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -53,6 +53,11 @@ public class ValueStringChangeActionOperation extends Operation implements Actio
return mTargetValueId;
}
+ /**
+ * The name of the operation used during serialization
+ *
+ * @return the operation serialized name
+ */
@NonNull
public String serializedName() {
return "VALUE_CHANGE";
@@ -85,6 +90,13 @@ public class ValueStringChangeActionOperation extends Operation implements Actio
context.overrideText(mTargetValueId, mValueId);
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param valueId the string id
+ * @param value the value to set (string id)`
+ */
public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
index c3624e5b3d88..8c1ffbd2c500 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
@@ -19,47 +19,37 @@ import android.annotation.NonNull;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
-import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
/** Set the min / max width dimension on a component */
-public class WidthInModifierOperation extends DrawBase2 implements ModifierOperation {
+public class WidthInModifierOperation extends DimensionInModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH_IN;
public static final String CLASS_NAME = "WidthInModifierOperation";
- /**
- * Read this operation and add it to the list of operations
- *
- * @param buffer the buffer to read
- * @param operations the list of operations that will be added to
- */
- public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
- Maker m = WidthInModifierOperation::new;
- read(m, buffer, operations);
+ public WidthInModifierOperation(float min, float max) {
+ super(OP_CODE, min, max);
}
- /**
- * Returns the min value
- *
- * @return minimum value
- */
- public float getMin() {
- return mV1;
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, getMin(), getMax());
}
/**
- * Returns the max value
+ * Read this operation and add it to the list of operations
*
- * @return maximum value
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
*/
- public float getMax() {
- return mV2;
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+ operations.add(new WidthInModifierOperation(v1, v2));
}
/**
@@ -81,11 +71,6 @@ public class WidthInModifierOperation extends DrawBase2 implements ModifierOpera
return CLASS_NAME;
}
- @Override
- protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
- apply(buffer, v1, v2);
- }
-
/**
* Populate the documentation with a description of this operation
*
@@ -98,14 +83,6 @@ public class WidthInModifierOperation extends DrawBase2 implements ModifierOpera
.field(DocumentedOperation.FLOAT, "max", "The maximum width, -1 if not applied");
}
- public WidthInModifierOperation(float min, float max) {
- super(min, max);
- mName = CLASS_NAME;
- }
-
- @Override
- public void paint(@NonNull PaintContext context) {}
-
/**
* Writes out the WidthInModifier to the buffer
*
@@ -114,7 +91,9 @@ public class WidthInModifierOperation extends DrawBase2 implements ModifierOpera
* @param y1 start y of the DrawOval
*/
public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
- write(buffer, OP_CODE, x1, y1);
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 532027ab2087..687238e62bac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -52,6 +52,13 @@ public class WidthModifierOperation extends DimensionModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param type the type of dimension rule (DimensionModifierOperation.Type)
+ * @param value the value of the dimension
+ */
public static void apply(@NonNull WireBuffer buffer, int type, float value) {
buffer.start(OP_CODE);
buffer.writeInt(type);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
index 35de33a9997a..52841a7e6779 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
@@ -55,6 +55,12 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation {
apply(buffer, mValue);
}
+ /**
+ * Serialize the string
+ *
+ * @param indent padding to display
+ * @param serializer append the string
+ */
// @Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(indent, "ZINDEX = [" + mValue + "]");
@@ -99,16 +105,33 @@ public class ZIndexModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param value the z-index value
+ */
public static void apply(WireBuffer buffer, float value) {
buffer.start(OP_CODE);
buffer.writeFloat(value);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
public static void read(WireBuffer buffer, List<Operation> operations) {
float value = buffer.readFloat();
operations.add(new ZIndexModifierOperation(value));
}
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
public static void documentation(DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Z-Index Modifier")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 95434696abdc..4c7f503e0bf8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -682,9 +682,9 @@ public class PaintBundle {
* @param radius Must be positive. The radius of the gradient.
* @param colors The sRGB colors distributed between the center and edge
* @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code>
- * 1.0f</code>. The relative position of each corresponding color in the colors array. If
- * <code>null</code>, colors are distributed evenly between the center and edge of the
- * circle.
+ * 1.0f</code>. The relative position of each corresponding color in the colors
+ * array. If <code>null</code>, colors are distributed evenly between the center and edge of
+ * the circle.
* @param tileMode The Shader tiling mode
*/
public void setRadialGradient(
@@ -808,7 +808,7 @@ public class PaintBundle {
}
/**
- * Set the color based the R,G,B,A values
+ * Set the color based the R,G,B,A values (Warning this does not support NaN ids)
*
* @param r red (0.0 to 1.0)
* @param g green (0.0 to 1.0)
@@ -816,7 +816,7 @@ public class PaintBundle {
* @param a alpha (0.0 to 1.0)
*/
public void setColor(float r, float g, float b, float a) {
- setColor((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (a * 255));
+ setColor(Utils.toARGB(a, r, g, b));
}
/**
@@ -897,6 +897,11 @@ public class PaintBundle {
mPos++;
}
+ /**
+ * set Filter Bitmap
+ *
+ * @param filter set to false to disable interpolation
+ */
public void setFilterBitmap(boolean filter) {
mArray[mPos] = FILTER_BITMAP | (filter ? (1 << 16) : 0);
mPos++;
@@ -944,6 +949,12 @@ public class PaintBundle {
}
}
+ /**
+ * Convert a blend mode integer as a string
+ *
+ * @param mode the blend mode
+ * @return the blend mode as a string
+ */
public static @NonNull String blendModeString(int mode) {
switch (mode) {
case PaintBundle.BLEND_MODE_CLEAR:
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
index ff6f45db5385..2812eed8a5ab 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/TextPaint.java
@@ -17,51 +17,250 @@ package com.android.internal.widget.remotecompose.core.operations.paint;
import android.annotation.NonNull;
+import java.util.Locale;
+
// TODO: this interface is unused. Delete it.
public interface TextPaint {
+
+ /**
+ * Helper to setColor(), that takes a,r,g,b and constructs the color int
+ *
+ * @param a The new alpha component (0..255) of the paint's color.
+ * @param r The new red component (0..255) of the paint's color.
+ * @param g The new green component (0..255) of the paint's color.
+ * @param b The new blue component (0..255) of the paint's color.
+ */
void setARGB(int a, int r, int g, int b);
+ /**
+ * Helper for setFlags(), setting or clearing the DITHER_FLAG bit Dithering affects how colors
+ * that are higher precision than the device are down-sampled. No dithering is generally faster,
+ * but higher precision colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
+ * distribute the error inherent in this process, to reduce the visual artifacts.
+ *
+ * @param dither true to set the dithering bit in flags, false to clear it
+ */
void setDither(boolean dither);
+ /**
+ * Set the paint's elegant height metrics flag. This setting selects font variants that have not
+ * been compacted to fit Latin-based vertical metrics, and also increases top and bottom bounds
+ * to provide more space.
+ *
+ * @param elegant set the paint's elegant metrics flag for drawing text.
+ */
void setElegantTextHeight(boolean elegant);
+ /**
+ * Set a end hyphen edit on the paint.
+ *
+ * <p>By setting end hyphen edit, the measurement and drawing is performed with modifying
+ * hyphenation at the end of line. For example, by passing character is appended at the end of
+ * line.
+ *
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN);
+ * paint.measureText("abc", 0, 3); // Returns the width of "abc-"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc-"
+ * </code>
+ * </pre>
+ *
+ * @param endHyphen a end hyphen edit value.
+ */
void setEndHyphenEdit(int endHyphen);
+ /**
+ * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
+ *
+ * @param fakeBoldText true to set the fakeBoldText bit in the paint's flags, false to clear it.
+ */
void setFakeBoldText(boolean fakeBoldText);
+ /**
+ * Set the paint's flags. Use the Flag enum to specific flag values.
+ *
+ * @param flags The new flag bits for the paint
+ */
void setFlags(int flags);
+ /**
+ * Set font feature settings.
+ *
+ * <p>The format is the same as the CSS font-feature-settings attribute: <a
+ * href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
+ * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
+ *
+ * @param settings the font feature settings string to use, may be null.
+ */
void setFontFeatureSettings(@NonNull String settings);
+ /**
+ * Set the paint's hinting mode. May be either
+ *
+ * @param mode The new hinting mode. (HINTING_OFF or HINTING_ON)
+ */
void setHinting(int mode);
+ /**
+ * Set the paint's letter-spacing for text. The default value is 0. The value is in 'EM' units.
+ * Typical values for slight expansion will be around 0.05. Negative values tighten text.
+ *
+ * @param letterSpacing set the paint's letter-spacing for drawing text.
+ */
void setLetterSpacing(float letterSpacing);
+ /**
+ * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit
+ *
+ * @param linearText true to set the linearText bit in the paint's flags, false to clear it.
+ */
void setLinearText(boolean linearText);
+ /**
+ * This draws a shadow layer below the main layer, with the specified offset and color, and blur
+ * radius. If radius is 0, then the shadow layer is removed.
+ *
+ * <p>Can be used to create a blurred shadow underneath text. Support for use with other drawing
+ * operations is constrained to the software rendering pipeline.
+ *
+ * <p>The alpha of the shadow will be the paint's alpha if the shadow color is opaque, or the
+ * alpha from the shadow color if not.
+ *
+ * @param radius the radius of the shadows
+ * @param dx the x offset of the shadow
+ * @param dy the y offset of the shadow
+ * @param shadowColor the color of the shadow
+ */
void setShadowLayer(float radius, float dx, float dy, int shadowColor);
+ /**
+ * Set a start hyphen edit on the paint.
+ *
+ * <p>By setting start hyphen edit, the measurement and drawing is performed with modifying
+ * hyphenation at the start of line. For example, by passing character is appended at the start
+ * of line.
+ *
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN);
+ * paint.measureText("abc", 0, 3); // Returns the width of "-abc"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "-abc"
+ * </code>
+ * </pre>
+ *
+ * The default value is 0 which is equivalent to
+ *
+ * @param startHyphen a start hyphen edit value.
+ */
void setStartHyphenEdit(int startHyphen);
+ /**
+ * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
+ *
+ * @param strikeThruText true to set the strikeThruText bit in the paint's flags, false to clear
+ * it.
+ */
void setStrikeThruText(boolean strikeThruText);
+ /**
+ * Set the paint's Cap.
+ *
+ * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
+ */
void setStrokeCap(int cap);
+ /**
+ * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
+ *
+ * @param subpixelText true to set the subpixelText bit in the paint's flags, false to clear it.
+ */
void setSubpixelText(boolean subpixelText);
+ /**
+ * Set the paint's text alignment. This controls how the text is positioned relative to its
+ * origin. LEFT align means that all of the text will be drawn to the right of its origin (i.e.
+ * the origin specifies the LEFT edge of the text) and so on.
+ *
+ * @param align set the paint's Align value for drawing text.
+ */
void setTextAlign(int align);
+ /**
+ * Set the text locale list to a one-member list consisting of just the locale.
+ *
+ * @param locale the paint's locale value for drawing text, must not be null.
+ */
void setTextLocale(int locale);
+ /**
+ * Set the text locale list.
+ *
+ * <p>The text locale list affects how the text is drawn for some languages.
+ *
+ * <p>For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
+ * then the text renderer will prefer to draw text using a Chinese font. Likewise, if the locale
+ * list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text renderer will
+ * prefer to draw text using a Japanese font. If the locale list contains both, the order those
+ * locales appear in the list is considered for deciding the font.
+ *
+ * <p>This distinction is important because Chinese and Japanese text both use many of the same
+ * Unicode code points but their appearance is subtly different for each language.
+ *
+ * <p>By default, the text locale list is initialized to a one-member list just containing the
+ * system locales. This assumes that the text to be rendered will most likely be in the user's
+ * preferred language.
+ *
+ * <p>If the actual language or languages of the text is/are known, then they can be provided to
+ * the text renderer using this method. The text renderer may attempt to guess the language
+ * script based on the contents of the text to be drawn independent of the text locale here.
+ * Specifying the text locales just helps it do a better job in certain ambiguous cases.
+ *
+ * @param localesArray the paint's locale list for drawing text, must not be null or empty.
+ */
void setTextLocales(int localesArray);
+ /**
+ * Set the paint's horizontal scale factor for text. The default value is 1.0. Values > 1.0 will
+ * stretch the text wider. Values < 1.0 will stretch the text narrower.
+ *
+ * @param scaleX set the paint's scale in X for drawing/measuring text.
+ */
void setTextScaleX(float scaleX);
+ /**
+ * Set the paint's text size. This value must be > 0
+ *
+ * @param textSize set the paint's text size in pixel units.
+ */
void setTextSize(float textSize);
+ /**
+ * Set the paint's horizontal skew factor for text. The default value is 0. For approximating
+ * oblique text, use values around -0.25.
+ *
+ * @param skewX set the paint's skew factor in X for drawing text.
+ */
void setTextSkewX(float skewX);
+ /**
+ * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit
+ *
+ * @param underlineText true to set the underlineText bit in the paint's flags, false to clear
+ * it.
+ */
void setUnderlineText(boolean underlineText);
+ /**
+ * Set the paint's extra word-spacing for text.
+ *
+ * <p>Increases the white space width between words with the given amount of pixels. The default
+ * value is 0.
+ *
+ * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
+ */
void setWordSpacing(float wordSpacing);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index b92f96f63eb9..704f6ce3447a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -22,6 +22,14 @@ import android.annotation.Nullable;
* unavailable
*/
public interface CollectionsAccess {
+
+ /**
+ * Get the float value in the array at the given index
+ *
+ * @param id the id of the float array
+ * @param index the index of the value
+ * @return
+ */
float getFloatValue(int id, int index);
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
index 07a3d8482db2..04beba3c4af8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
@@ -28,6 +28,12 @@ public class DataMap {
mIds = ids;
}
+ /**
+ * Return position for given string
+ *
+ * @param str string
+ * @return position associated with the string
+ */
public int getPos(@NonNull String str) {
for (int i = 0; i < mNames.length; i++) {
String name = mNames[i];
@@ -38,10 +44,22 @@ public class DataMap {
return -1;
}
+ /**
+ * Return type for given index
+ *
+ * @param pos index
+ * @return type at index
+ */
public byte getType(int pos) {
return mTypes[pos];
}
+ /**
+ * Return id for given index
+ *
+ * @param pos index
+ * @return id at index
+ */
public int getId(int pos) {
return mIds[pos];
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
index 98ee91b370e0..93af8bcef206 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -76,6 +76,20 @@ public class ImageScaling {
adjustDrawToType();
}
+ /**
+ * Setup the ImageScaling
+ *
+ * @param srcLeft src left
+ * @param srcTop src top
+ * @param srcRight src right
+ * @param srcBottom src bottom
+ * @param dstLeft destination left
+ * @param dstTop destination top
+ * @param dstRight destination right
+ * @param dstBottom destination bottom
+ * @param type type of scaling
+ * @param scale scale factor
+ */
public void setup(
float srcLeft,
float srcTop,
@@ -215,6 +229,12 @@ public class ImageScaling {
}
}
+ /**
+ * Utility to map a string to the given type
+ *
+ * @param type
+ * @return
+ */
@NonNull
public static String typeToString(int type) {
String[] typeString = {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index b9aa88146f2a..257eb0645938 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -39,12 +39,20 @@ public class IntMap<T> {
}
}
+ /** Clear the map */
public void clear() {
Arrays.fill(mKeys, NOT_PRESENT);
mValues.clear();
mSize = 0;
}
+ /**
+ * Insert the value into the map with the given key
+ *
+ * @param key
+ * @param value
+ * @return
+ */
@Nullable
public T put(int key, @NonNull T value) {
if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
@@ -54,6 +62,12 @@ public class IntMap<T> {
return insert(key, value);
}
+ /**
+ * Return the value associated with the given key
+ *
+ * @param key
+ * @return
+ */
@Nullable
public T get(int key) {
int index = findKey(key);
@@ -62,6 +76,11 @@ public class IntMap<T> {
} else return mValues.get(index);
}
+ /**
+ * Return the size of the map
+ *
+ * @return
+ */
public int size() {
return mSize;
}
@@ -117,6 +136,12 @@ public class IntMap<T> {
}
}
+ /**
+ * Remote the key from the map
+ *
+ * @param key
+ * @return
+ */
@Nullable
public T remove(int key) {
int index = hash(key) % mKeys.length;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
index 0616cc7306f5..1f98f62a20b3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -41,18 +41,42 @@ public class NanMap {
public static final float CLOSE_NAN = Utils.asNan(CLOSE);
public static final float DONE_NAN = Utils.asNan(DONE);
+ /**
+ * Returns true if the float id is a system variable
+ *
+ * @param value the id encoded as float NaN
+ * @return
+ */
public static boolean isSystemVariable(float value) {
return (fromNaN(value) >> 20) == 0;
}
+ /**
+ * Returns true if the float id is a normal variable
+ *
+ * @param value the id encoded as float NaN
+ * @return
+ */
public static boolean isNormalVariable(float value) {
return (fromNaN(value) >> 20) == 1;
}
+ /**
+ * Returns true if the float id is a data variable
+ *
+ * @param value the id encoded as float NaN
+ * @return
+ */
public static boolean isDataVariable(float value) {
return (fromNaN(value) >> 20) == 2;
}
+ /**
+ * Returns true if the float id is an operation variable
+ *
+ * @param value the id encoded as float NaN
+ * @return
+ */
public static boolean isOperationVariable(float value) {
return (fromNaN(value) >> 20) == 3;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
index cc6c2a6ac7a9..928e9ea1a280 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/AccessibleComponent.java
@@ -134,6 +134,12 @@ public interface AccessibleComponent extends AccessibilitySemantics {
return mDescription;
}
+ /**
+ * Map int value to Role enum value
+ *
+ * @param i int value
+ * @return corresponding enum value
+ */
public static Role fromInt(int i) {
if (i < UNKNOWN.ordinal()) {
return Role.values()[i];
diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
new file mode 100644
index 000000000000..2be8057ce097
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.serialize;
+
+import android.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/** Represents a serializer for a map */
+public interface MapSerializer {
+
+ /**
+ * Add a list entry to this map. The List values can be any primitive, List, Map, or
+ * Serializable
+ *
+ * @param key The key
+ * @param value The list
+ */
+ <T> void add(String key, @Nullable List<T> value);
+
+ /**
+ * Add a map entry to this map. The map values can be any primitive, List, Map, or Serializable
+ *
+ * @param key The key
+ * @param value The list
+ */
+ <T> void add(String key, @Nullable Map<String, T> value);
+
+ /**
+ * Adds any Serializable type to this map
+ *
+ * @param key The key
+ * @param value The Serializable
+ */
+ void add(String key, @Nullable Serializable value);
+
+ /**
+ * Adds a String entry
+ *
+ * @param key The key
+ * @param value The String
+ */
+ void add(String key, @Nullable String value);
+
+ /**
+ * Adds a Byte entry
+ *
+ * @param key The key
+ * @param value The Byte
+ */
+ void add(String key, @Nullable Byte value);
+
+ /**
+ * Adds a Short entry
+ *
+ * @param key The key
+ * @param value The Short
+ */
+ void add(String key, @Nullable Short value);
+
+ /**
+ * Adds an Integer entry
+ *
+ * @param key The key
+ * @param value The Integer
+ */
+ void add(String key, @Nullable Integer value);
+
+ /**
+ * Adds a Long entry
+ *
+ * @param key The key
+ * @param value The Long
+ */
+ void add(String key, @Nullable Long value);
+
+ /**
+ * Adds a Float entry
+ *
+ * @param key The key
+ * @param value The Float
+ */
+ void add(String key, @Nullable Float value);
+
+ /**
+ * Adds a Double entry
+ *
+ * @param key The key
+ * @param value The Double
+ */
+ void add(String key, @Nullable Double value);
+
+ /**
+ * Adds a Boolean entry
+ *
+ * @param key The key
+ * @param value The Boolean
+ */
+ void add(String key, @Nullable Boolean value);
+
+ /**
+ * Adds a Enum entry
+ *
+ * @param key The key
+ * @param value The Enum
+ */
+ <T extends Enum<T>> void add(String key, @Nullable Enum<T> value);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java
new file mode 100644
index 000000000000..820cdccfeabb
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/Serializable.java
@@ -0,0 +1,27 @@
+/*
+ * 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.internal.widget.remotecompose.core.serialize;
+
+/** Implementation for any class that wants to serialize itself */
+public interface Serializable {
+
+ /**
+ * Called when this class is to be serialized
+ *
+ * @param serializer Interface to the serializer
+ */
+ void serialize(MapSerializer serializer);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index b17e3dc82d50..77f4b6a83eef 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -39,12 +39,13 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.remotecompose.accessibility.RemoteComposeTouchHelper;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.RemoteContextAware;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
/** A view to to display and play RemoteCompose documents */
-public class RemoteComposePlayer extends FrameLayout {
+public class RemoteComposePlayer extends FrameLayout implements RemoteContextAware {
private RemoteComposeCanvas mInner;
private static final int MAX_SUPPORTED_MAJOR_VERSION = MAJOR_VERSION;
@@ -65,6 +66,11 @@ public class RemoteComposePlayer extends FrameLayout {
init(context, attrs, defStyleAttr);
}
+ @Override
+ public RemoteContext getRemoteContext() {
+ return mInner.getRemoteContext();
+ }
+
/**
* @inheritDoc
*/
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 9d385ddafe19..14349b028819 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -185,7 +185,7 @@ public class AndroidRemoteContext extends RemoteContext {
@Override
public void runAction(int id, @NonNull String metadata) {
- mDocument.performClick(id);
+ mDocument.performClick(this, id);
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 334ba62636ff..f76794fc0372 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -147,7 +147,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
param.leftMargin = (int) area.getLeft();
param.topMargin = (int) area.getTop();
viewArea.setOnClickListener(
- view1 -> mDocument.getDocument().performClick(area.getId()));
+ view1 -> mDocument.getDocument().performClick(mARContext, area.getId()));
addView(viewArea, param);
}
if (!clickAreas.isEmpty()) {
@@ -303,6 +303,10 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mARContext.setUseChoreographer(value);
}
+ public RemoteContext getRemoteContext() {
+ return mARContext;
+ }
+
public interface ClickCallbacks {
void click(int id, String metadata);
}
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
index 41432294b3c2..9b97c8feaf12 100644
--- a/core/tests/coretests/src/android/app/NotificationManagerTest.java
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -19,7 +19,6 @@ package android.app;
import static com.google.common.truth.Truth.assertThat;
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.Mockito.atLeast;
@@ -304,9 +303,8 @@ public class NotificationManagerTest {
// It doesn't matter what the returned contents are, as long as we return a channel.
// This setup must set up getNotificationChannels(), as that's the method called.
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
- anyInt(), anyBoolean())).thenReturn(
- new ParceledListSlice<>(List.of(exampleChannel())));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+ anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
// ask for the same channel 100 times without invalidating the cache
for (int i = 0; i < 100; i++) {
@@ -318,7 +316,7 @@ public class NotificationManagerTest {
NotificationChannel unused = mNotificationManager.getNotificationChannel("id");
verify(mNotificationManager.mBackendService, times(2))
- .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+ .getNotificationChannels(any(), any(), anyInt());
}
@Test
@@ -331,24 +329,23 @@ public class NotificationManagerTest {
NotificationChannel c2 = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_NONE);
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
- anyInt(), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+ anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
assertThat(mNotificationManager.getNotificationChannel("id1")).isEqualTo(c1);
assertThat(mNotificationManager.getNotificationChannel("id2")).isEqualTo(c2);
assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
verify(mNotificationManager.mBackendService, times(1))
- .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+ .getNotificationChannels(any(), any(), anyInt());
}
@Test
@EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
public void getNotificationChannels_cachedUntilInvalidated() throws Exception {
NotificationManager.invalidateNotificationChannelCache();
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
- anyInt(), anyBoolean())).thenReturn(
- new ParceledListSlice<>(List.of(exampleChannel())));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+ anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
// ask for channels 100 times without invalidating the cache
for (int i = 0; i < 100; i++) {
@@ -360,7 +357,7 @@ public class NotificationManagerTest {
List<NotificationChannel> res = mNotificationManager.getNotificationChannels();
verify(mNotificationManager.mBackendService, times(2))
- .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+ .getNotificationChannels(any(), any(), anyInt());
assertThat(res).containsExactlyElementsIn(List.of(exampleChannel()));
}
@@ -378,9 +375,8 @@ public class NotificationManagerTest {
NotificationChannel c2 = new NotificationChannel("other", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
- anyInt(), anyBoolean())).thenReturn(
- new ParceledListSlice<>(List.of(c1, conv1, c2)));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+ anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, conv1, c2)));
// Lookup for channel c1 and c2: returned as expected
assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(c1);
@@ -397,9 +393,9 @@ public class NotificationManagerTest {
// Lookup of a nonexistent channel is null
assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
- // All of that should have been one call to getOrCreateNotificationChannels()
+ // All of that should have been one call to getNotificationChannels()
verify(mNotificationManager.mBackendService, times(1))
- .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+ .getNotificationChannels(any(), any(), anyInt());
}
@Test
@@ -419,12 +415,12 @@ public class NotificationManagerTest {
NotificationChannel channel3 = channel1.copy();
channel3.setName("name3");
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
- eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel1)));
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg2),
- eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel2)));
- when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
- eq(userId1), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel3)));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+ eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel1)));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg2),
+ eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel2)));
+ when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+ eq(userId1))).thenReturn(new ParceledListSlice<>(List.of(channel3)));
// set our context to pretend to be from package 1 and userId 0
mContext.setParameters(pkg1, pkg1, userId);
@@ -440,7 +436,7 @@ public class NotificationManagerTest {
// Those should have been three different calls
verify(mNotificationManager.mBackendService, times(3))
- .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
+ .getNotificationChannels(any(), any(), anyInt());
}
@Test
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index cef6970ba25a..c40137f1bd34 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1054,8 +1054,7 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = mView.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
mView.invalidate();
- viewRootImpl.notifyInsetsAnimationRunningStateChanged(true, 0 /* animationType */,
- 0 /* insetsTypes */ /* areOtherAnimationsRunning */);
+ viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
mView.invalidate();
});
sInstrumentation.waitForIdleSync();
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 a65e69eee5fe..c40a276cb7bd 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
@@ -56,6 +56,7 @@ import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
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.BubbleInfo;
import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
import com.android.wm.shell.taskview.TaskView;
@@ -1093,7 +1094,7 @@ public class Bubble implements BubbleViewProvider {
* intent for an app. In this case we don't show a badge on the icon.
*/
public boolean isAppLaunchIntent() {
- if (Flags.enableBubbleAnything() && mAppIntent != null) {
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble() && mAppIntent != null) {
return mAppIntent.hasCategory("android.intent.category.LAUNCHER");
}
return false;
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 5f2b95f7b137..5cd04b11bbfd 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
@@ -1433,7 +1433,7 @@ public class BubbleController implements ConfigurationChangeListener,
* @param info the shortcut info for the bubble.
*/
public void expandStackAndSelectBubble(ShortcutInfo info) {
- if (!Flags.enableBubbleAnything()) return;
+ if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
if (b.isInflated()) {
@@ -1450,7 +1450,7 @@ public class BubbleController implements ConfigurationChangeListener,
* @param intent the intent for the bubble.
*/
public void expandStackAndSelectBubble(Intent intent, UserHandle user) {
- if (!Flags.enableBubbleAnything()) return;
+ if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
Bubble b = mBubbleData.getOrCreateBubble(intent, user); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
if (b.isInflated()) {
@@ -2516,7 +2516,7 @@ public class BubbleController implements ConfigurationChangeListener,
* @param entry the entry to bubble.
*/
static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
- if (Flags.enableBubbleAnything()) return true;
+ if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) return true;
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 13f8e9ef9dd3..e98d53e85b94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -71,6 +71,7 @@ import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.taskview.TaskView;
import java.io.PrintWriter;
@@ -226,7 +227,8 @@ public class BubbleExpandedView extends LinearLayout {
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
- || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+ || (mBubble.getShortcutInfo() != null
+ && BubbleAnythingFlagHelper.enableCreateAnyBubble()));
if (mBubble.isAppBubble()) {
Context context =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 62995319db80..086c91985ae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -137,14 +137,15 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
// Update bitmap
val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
val drawable = AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)
- bitmap = iconFactory.createBadgedIconBitmap(drawable).icon
+ val bubbleBitmapScale = FloatArray(1)
+ bitmap = iconFactory.getBubbleBitmap(drawable, bubbleBitmapScale)
// Update dot path
dotPath =
PathParser.createPathFromPathData(
res.getString(com.android.internal.R.string.config_icon_mask)
)
- val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable)
+ val scale = bubbleBitmapScale[0]
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
matrix.setScale(
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 ae84f449c0e4..a6b858500dcb 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
@@ -36,7 +36,7 @@ import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.android.internal.protolog.ProtoLog;
-import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.taskview.TaskView;
/**
@@ -108,7 +108,8 @@ public class BubbleTaskViewHelper {
options.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
- || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+ || (mBubble.getShortcutInfo() != null
+ && BubbleAnythingFlagHelper.enableCreateAnyBubble()));
if (mBubble.getPreparingTransition() != null) {
mBubble.getPreparingTransition().surfaceCreated();
} else if (mBubble.isAppBubble()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 4bbe22a59315..6657c9e4b9a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY;
@@ -1023,7 +1024,7 @@ public abstract class WMShellModule {
DesktopModeCompatPolicy desktopModeCompatPolicy) {
if (!DesktopModeStatus.canEnterDesktopMode(context)
|| !ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
- || !Flags.enableDesktopSystemDialogsTransitions()) {
+ || !ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS.isTrue()) {
return Optional.empty();
}
return Optional.of(
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 ca71cf303a1c..5b206dedee49 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
@@ -56,6 +56,7 @@ import android.view.WindowManager.TRANSIT_TO_FRONT
import android.widget.Toast
import android.window.DesktopModeFlags
import android.window.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
import android.window.RemoteTransition
@@ -1515,7 +1516,7 @@ class DesktopTasksController(
private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
- if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+ if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
val intent = Intent(context, DesktopWallpaperActivity::class.java)
if (
desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
@@ -1578,7 +1579,7 @@ class DesktopTasksController(
private fun removeWallpaperActivity(wct: WindowContainerTransaction, displayId: Int) {
desktopWallpaperActivityTokenProvider.getToken(displayId)?.let { token ->
logV("removeWallpaperActivity")
- if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+ if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
wct.reorder(token, /* onTop= */ false)
} else {
wct.removeTask(token)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 14c8429766cc..b3648699ed0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -27,6 +27,7 @@ import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DesktopModeFlags
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
@@ -275,7 +276,7 @@ class DesktopTasksTransitionObserver(
desktopWallpaperActivityTokenProvider
.getToken(lastSeenTransitionToCloseWallpaper.displayId)
?.let { wallpaperActivityToken ->
- if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+ if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
transitions.startTransition(
TRANSIT_TO_BACK,
WindowContainerTransaction()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index 13576aa42737..a5ba6612bb1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -21,9 +21,9 @@ import android.content.Context
import android.content.pm.UserInfo
import android.os.UserManager
import android.util.SparseArray
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM
import androidx.core.util.forEach
import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -68,7 +68,7 @@ class DesktopUserRepositories(
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(::onInit, this)
}
- if (Flags.enableDesktopWindowingHsum()) {
+ if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
}
}
@@ -80,7 +80,7 @@ class DesktopUserRepositories(
/** Returns [DesktopRepository] for the parent user id. */
fun getProfile(profileId: Int): DesktopRepository {
- if (Flags.enableDesktopWindowingHsum()) {
+ if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
for ((uid, profileIds) in userIdToProfileIdsMap) {
if (profileId in profileIds) {
return desktopRepoByUserId.getOrCreate(uid)
@@ -101,14 +101,14 @@ class DesktopUserRepositories(
override fun onUserChanged(newUserId: Int, userContext: Context) {
logD("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
userId = newUserId
- if (Flags.enableDesktopWindowingHsum()) {
+ if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
sanitizeUsers()
}
}
override fun onUserProfilesChanged(profiles: MutableList<UserInfo>) {
logD("onUserProfilesChanged profiles=%s", profiles.toString())
- if (Flags.enableDesktopWindowingHsum()) {
+ if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
// TODO(b/366397912): Remove all persisted profile data when the profile changes.
userIdToProfileIdsMap[userId] = profiles.map { it.id }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2cf574158358..7e7a793298e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -466,7 +466,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
mRecentsTransitionHandler.addTransitionStateListener(
new DesktopModeRecentsTransitionStateListener());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index e5c989ed5f97..053850480ecc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -49,6 +49,7 @@ import com.android.window.flags.Flags
import com.android.wm.shell.R
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.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -645,7 +646,7 @@ class HandleMenu(
private fun bindWindowingPill(style: MenuStyle) {
windowingPill.background.setTint(style.backgroundColor)
- if (!com.android.wm.shell.Flags.enableBubbleAnything()) {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
floatingBtn.visibility = View.GONE
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index 70c0b54462e3..22bc9782170b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -5,6 +5,7 @@ import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.graphics.PointF
import android.graphics.Rect
+import android.view.Choreographer
import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.VelocityTracker
@@ -48,7 +49,7 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
t.setScale(taskSurface, scale, scale)
.setCornerRadius(taskSurface, cornerRadius)
.setScale(taskSurface, scale, scale)
- .setCornerRadius(taskSurface, cornerRadius)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
.setPosition(taskSurface, position.x, position.y)
.apply()
}
@@ -96,6 +97,7 @@ class MoveToDesktopAnimator @JvmOverloads constructor(
setTaskPosition(ev.rawX, ev.rawY)
val t = transactionFactory()
t.setPosition(taskSurface, position.x, position.y)
+ t.setFrameTimeline(Choreographer.getInstance().vsyncId)
t.apply()
}
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
index 834814ccbc6b..1c6b1ebd1535 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/settingslib_expressive_top_intro.xml
@@ -18,7 +18,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:filterTouchesWhenObscured="true">
<com.android.settingslib.widget.CollapsableTextView
android:id="@+id/collapsable_text_view"
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index e3d7902f34b2..00973811dbf0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -84,7 +84,11 @@ public final class ActionDisabledByAdminControllerFactory {
return false;
}
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
- return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ final SupervisionManager sm =
+ android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()
+ ? context.getSystemService(SupervisionManager.class)
+ : null;
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm, sm,
BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c47bf628002d..7c588b3834a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4072,7 +4072,7 @@ public class SettingsProvider extends ContentProvider {
@VisibleForTesting
final class UpgradeController {
- private static final int SETTINGS_VERSION = 226;
+ private static final int SETTINGS_VERSION = 227;
private final int mUserId;
@@ -6266,6 +6266,51 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 226;
}
+ // Version 226: Introduces dreaming while postured setting and migrates user from
+ // docked dream trigger to postured dream trigger.
+ if (currentVersion == 226) {
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting dreamOnDock = secureSettings.getSettingLocked(
+ Secure.SCREENSAVER_ACTIVATE_ON_DOCK);
+ final Setting dreamsEnabled = secureSettings.getSettingLocked(
+ Secure.SCREENSAVER_ENABLED);
+ final boolean dreamOnPosturedDefault = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault);
+ final boolean dreamsEnabledByDefault = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsEnabledByDefault);
+
+ if (dreamOnPosturedDefault && !dreamOnDock.isNull()
+ && dreamOnDock.getValue().equals("1")) {
+ // Disable dock activation and enable postured.
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ "0",
+ null,
+ true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ "1",
+ null,
+ true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+
+ // Disable dreams overall, so user doesn't start to unexpectedly see dreams
+ // enabled when postured.
+ if (!dreamsEnabledByDefault && !dreamsEnabled.isNull()
+ && dreamsEnabled.getValue().equals("1")) {
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.SCREENSAVER_ENABLED,
+ "0",
+ null,
+ true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ currentVersion = 227;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6491bf5c8f5b..72ae76a45cac 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1171,5 +1171,16 @@
android:exported="false"
/>
+ <service
+ android:name="com.google.android.systemui.lowlightclock.LowLightClockDreamService"
+ android:enabled="false"
+ android:exported="false"
+ android:directBootAware="true"
+ android:permission="android.permission.BIND_DREAM_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.dreams.DreamService" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a92df8026715..f0c855753292 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -541,6 +541,16 @@ flag {
}
flag {
+ name: "face_scanning_animation_npe_fix"
+ namespace: "systemui"
+ description: "Fix for the face scanning animation NPE"
+ bug: "392032258"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "indication_text_a11y_fix"
namespace: "systemui"
description: "add double shadow to the indication text at the bottom of the lock screen"
@@ -623,13 +633,6 @@ flag {
}
flag {
- name: "quick_settings_visual_haptics_longpress"
- namespace: "systemui"
- description: "Enable special visual and haptic effects for quick settings tiles with long-press actions"
- bug: "229856884"
-}
-
-flag {
name: "switch_user_on_bg"
namespace: "systemui"
description: "Does user switching on a background thread"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index d7545cb07849..22556777c40f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -26,7 +26,6 @@ import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTran
import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition
-import com.android.systemui.scene.ui.composable.transitions.notificationsShadeToQuickSettingsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition
@@ -172,13 +171,6 @@ val SceneContainerTransitions = transitions {
toQuickSettingsShadeTransition()
}
from(
- Overlays.NotificationsShade,
- to = Overlays.QuickSettingsShade,
- cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
- ) {
- notificationsShadeToQuickSettingsShadeTransition()
- }
- from(
Scenes.Gone,
to = Overlays.NotificationsShade,
key = SlightlyFasterShadeCollapse,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index b087ecf1a488..6d75c4ca3a38 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -46,7 +46,7 @@ import androidx.test.filters.SmallTest;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -86,7 +86,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Mock
private IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
@Mock
private SecureSettings mSecureSettings;
@Mock
@@ -114,7 +114,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
assertNotNull(mTestableLooper);
mMagnification = new MagnificationImpl(getContext(),
mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
- mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
+ mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class),
mA11yLogger, mIWindowManager, mAccessibilityManager,
mViewCaptureAwareWindowManager);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
index 5e023a203267..6899d23bcfd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -34,7 +34,7 @@ import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -68,7 +68,7 @@ class KeyboardTouchpadEduInteractorParameterizedTest(private val gestureType: Ge
private val keyboardRepository = kosmos.keyboardRepository
private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
private val userRepository = kosmos.fakeUserRepository
- private val overviewProxyService = kosmos.mockOverviewProxyService
+ private val launcherProxyService = kosmos.mockLauncherProxyService
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
private val eduClock = kosmos.fakeEduClock
@@ -519,8 +519,8 @@ class KeyboardTouchpadEduInteractorParameterizedTest(private val gestureType: Ge
}
private fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: GestureType) {
- val listenerCaptor = argumentCaptor<OverviewProxyListener>()
- verify(overviewProxyService).addCallback(listenerCaptor.capture())
+ val listenerCaptor = argumentCaptor<LauncherProxyListener>()
+ verify(launcherProxyService).addCallback(listenerCaptor.capture())
listenerCaptor.firstValue.updateContextualEduStats(isTrackpadGesture, gestureType)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 692b9c67f322..dc393c01dce1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -32,7 +32,7 @@ import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.google.common.truth.Truth.assertThat
@@ -58,7 +58,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private val touchpadRepository = kosmos.touchpadRepository
private val keyboardRepository = kosmos.keyboardRepository
private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
- private val overviewProxyService = kosmos.mockOverviewProxyService
+ private val launcherProxyService = kosmos.mockLauncherProxyService
private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
private val eduClock = kosmos.fakeEduClock
@@ -167,16 +167,16 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
keyboardRepository.setIsAnyKeyboardConnected(true)
}
- private fun getOverviewProxyListener(): OverviewProxyListener {
- val listenerCaptor = argumentCaptor<OverviewProxyListener>()
- verify(overviewProxyService).addCallback(listenerCaptor.capture())
+ private fun getLauncherProxyListener(): LauncherProxyListener {
+ val listenerCaptor = argumentCaptor<LauncherProxyListener>()
+ verify(launcherProxyService).addCallback(listenerCaptor.capture())
return listenerCaptor.firstValue
}
private fun TestScope.triggerEducation(gestureType: GestureType) {
// Increment max number of signal to try triggering education
for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
- val listener = getOverviewProxyListener()
+ val listener = getLauncherProxyListener()
listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
}
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
index 698fac107a1d..4d81cb0ce726 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyboard.shortcut.data.repository
import android.content.Context
import android.content.Context.INPUT_SERVICE
import android.content.Intent
+import android.hardware.input.FakeInputManager
import android.hardware.input.InputGestureData
import android.hardware.input.InputManager
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
@@ -57,13 +58,14 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
private val secondaryUserContext: Context = mock()
private var activeUserContext: Context = primaryUserContext
- private val kosmos = testKosmos().also {
- it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
- }
+ private val kosmos =
+ testKosmos().also {
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
+ }
private val inputManager = kosmos.fakeInputManager.inputManager
private val broadcastDispatcher = kosmos.broadcastDispatcher
- private val inputManagerForSecondaryUser: InputManager = mock()
+ private val inputManagerForSecondaryUser: InputManager = FakeInputManager().inputManager
private val testScope = kosmos.testScope
private val testHelper = kosmos.shortcutHelperTestHelper
private val customInputGesturesRepository = kosmos.customInputGesturesRepository
@@ -94,9 +96,10 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
fun customInputGestures_initialValueReturnsDataFromAPI() {
testScope.runTest {
val customInputGestures = listOf(allAppsInputGestureData)
- whenever(
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).then { return@then customInputGestures }
+ whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+ .then {
+ return@then customInputGestures
+ }
val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
@@ -108,9 +111,10 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
fun customInputGestures_isUpdatedToMostRecentDataAfterNewGestureIsAdded() {
testScope.runTest {
var customInputGestures = listOf<InputGestureData>()
- whenever(
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).then { return@then customInputGestures }
+ whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+ .then {
+ return@then customInputGestures
+ }
whenever(inputManager.addCustomInputGesture(any())).then { invocation ->
val inputGesture = invocation.getArgument<InputGestureData>(0)
customInputGestures = customInputGestures + inputGesture
@@ -129,10 +133,10 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
fun retrieveCustomInputGestures_retrievesMostRecentData() {
testScope.runTest {
var customInputGestures = listOf<InputGestureData>()
- whenever(
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).then { return@then customInputGestures }
-
+ whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+ .then {
+ return@then customInputGestures
+ }
assertThat(customInputGesturesRepository.retrieveCustomInputGestures()).isEmpty()
@@ -143,24 +147,38 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() {
}
}
+ @Test
+ fun getInputGestureByTrigger_returnsInputGestureFromInputManager() =
+ testScope.runTest {
+ inputManager.addCustomInputGesture(allAppsInputGestureData)
+
+ val inputGestureData =
+ customInputGesturesRepository.getInputGestureByTrigger(
+ allAppsInputGestureData.trigger
+ )
+
+ assertThat(inputGestureData).isEqualTo(allAppsInputGestureData)
+ }
+
private fun setCustomInputGesturesForPrimaryUser(vararg inputGesture: InputGestureData) {
- whenever(
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).thenReturn(inputGesture.toList())
+ whenever(inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY))
+ .thenReturn(inputGesture.toList())
}
private fun setCustomInputGesturesForSecondaryUser(vararg inputGesture: InputGestureData) {
whenever(
- inputManagerForSecondaryUser.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- ).thenReturn(inputGesture.toList())
+ inputManagerForSecondaryUser.getCustomInputGestures(
+ /* filter= */ InputGestureData.Filter.KEY
+ )
+ )
+ .thenReturn(inputGesture.toList())
}
private fun switchToSecondaryUser() {
activeUserContext = secondaryUserContext
broadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
- Intent(Intent.ACTION_USER_SWITCHED)
+ Intent(Intent.ACTION_USER_SWITCHED),
)
}
-
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index 4cfb26e6555b..522572dcffb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -24,6 +24,7 @@ import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
import android.hardware.input.fakeInputManager
import android.platform.test.annotations.DisableFlags
@@ -336,28 +337,6 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
}
}
- private suspend fun customizeShortcut(
- customizationRequest: ShortcutCustomizationRequestInfo,
- keyCombination: KeyCombination? = null,
- ): ShortcutCustomizationRequestResult {
- repo.onCustomizationRequested(customizationRequest)
- repo.updateUserKeyCombination(keyCombination)
-
- return when (customizationRequest) {
- is SingleShortcutCustomization.Add -> {
- repo.confirmAndSetShortcutCurrentlyBeingCustomized()
- }
-
- is SingleShortcutCustomization.Delete -> {
- repo.deleteShortcutCurrentlyBeingCustomized()
- }
-
- else -> {
- ShortcutCustomizationRequestResult.ERROR_OTHER
- }
- }
- }
-
@Test
@EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
fun categories_isUpdatedAfterCustomShortcutsAreReset() {
@@ -387,10 +366,66 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
}
}
+ @Test
+ fun selectedKeyCombinationIsAvailable_whenTriggerIsNotRegisteredInInputManager() =
+ testScope.runTest {
+ helper.toggle(deviceId = 123)
+ repo.onCustomizationRequested(allAppsShortcutAddRequest)
+ repo.updateUserKeyCombination(standardKeyCombination)
+
+ assertThat(repo.isSelectedKeyCombinationAvailable()).isTrue()
+ }
+
+ @Test
+ fun selectedKeyCombinationIsNotAvailable_whenTriggerIsRegisteredInInputManager() =
+ testScope.runTest {
+ inputManager.addCustomInputGesture(buildInputGestureWithStandardKeyCombination())
+
+ helper.toggle(deviceId = 123)
+ repo.onCustomizationRequested(allAppsShortcutAddRequest)
+ repo.updateUserKeyCombination(standardKeyCombination)
+
+ assertThat(repo.isSelectedKeyCombinationAvailable()).isFalse()
+ }
+
private fun setApiAppLaunchBookmarks(appLaunchBookmarks: List<InputGestureData>) {
whenever(inputManager.appLaunchBookmarks).thenReturn(appLaunchBookmarks)
}
+ private suspend fun customizeShortcut(
+ customizationRequest: ShortcutCustomizationRequestInfo,
+ keyCombination: KeyCombination? = null,
+ ): ShortcutCustomizationRequestResult {
+ repo.onCustomizationRequested(customizationRequest)
+ repo.updateUserKeyCombination(keyCombination)
+
+ return when (customizationRequest) {
+ is SingleShortcutCustomization.Add -> {
+ repo.confirmAndSetShortcutCurrentlyBeingCustomized()
+ }
+
+ is SingleShortcutCustomization.Delete -> {
+ repo.deleteShortcutCurrentlyBeingCustomized()
+ }
+
+ else -> {
+ ShortcutCustomizationRequestResult.ERROR_OTHER
+ }
+ }
+ }
+
+ private fun buildInputGestureWithStandardKeyCombination() =
+ InputGestureData.Builder()
+ .setKeyGestureType(KEY_GESTURE_TYPE_HOME)
+ .setTrigger(
+ createKeyTrigger(
+ /* keycode= */ standardKeyCombination.keyCode!!,
+ /* modifierState= */ standardKeyCombination.modifiers and
+ ALL_SUPPORTED_MODIFIERS,
+ )
+ )
+ .build()
+
private fun simpleInputGestureDataForAppLaunchShortcut(
keyCode: Int = KEYCODE_A,
modifiers: Int = META_CTRL_ON or META_ALT_ON,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index d9d34f5ace7b..6eef5eb09812 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -18,11 +18,15 @@ package com.android.systemui.keyboard.shortcut.ui.viewmodel
import android.content.Context
import android.content.Context.INPUT_SERVICE
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
import android.hardware.input.fakeInputManager
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.META_CTRL_ON
+import android.view.KeyEvent.META_META_ON
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -30,7 +34,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithoutActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyUpEventWithActionKeyPressed
@@ -44,16 +47,17 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiSt
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -63,7 +67,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
private val mockUserContext: Context = mock()
private val kosmos =
- testKosmos().also {
+ testKosmos().useUnconfinedTestDispatcher().also {
it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
private val testScope = kosmos.testScope
@@ -75,6 +79,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
fun setup() {
helper.showFromActivity()
whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+ testScope.backgroundScope.launch { viewModel.activate() }
}
@Test
@@ -146,8 +151,6 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
fun uiState_becomeInactiveAfterSuccessfullySettingShortcut() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.addCustomInputGesture(any()))
- .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS)
openAddShortcutDialogAndSetShortcut()
@@ -166,11 +169,38 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
}
@Test
- fun uiState_errorMessage_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
+ fun uiState_errorMessage_onKeyPressed_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
testScope.runTest {
+ inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+
+ openAddShortcutDialogAndPressKeyCombination()
+
+ assertThat((uiState as AddShortcutDialog).errorMessage)
+ .isEqualTo(
+ context.getString(
+ R.string.shortcut_customizer_key_combination_in_use_error_message
+ )
+ )
+ }
+ }
+
+ @Test
+ fun uiState_errorMessage_onKeyPressed_isEmpty_whenKeyCombinationIsAvailable() {
+ testScope.runTest {
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+
+ openAddShortcutDialogAndPressKeyCombination()
+
+ assertThat((uiState as AddShortcutDialog).errorMessage).isEmpty()
+ }
+ }
+
+ @Test
+ fun uiState_errorMessage_onSetShortcut_isKeyCombinationInUse_whenKeyCombinationAlreadyExists() {
+ testScope.runTest {
+ inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.addCustomInputGesture(any()))
- .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS)
openAddShortcutDialogAndSetShortcut()
@@ -184,11 +214,12 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
}
@Test
- fun uiState_errorMessage_isKeyCombinationInUse_whenKeyCombinationIsReserved() {
+ fun uiState_errorMessage_onSetShortcut_isKeyCombinationInUse_whenKeyCombinationIsReserved() {
testScope.runTest {
+ inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+ kosmos.fakeInputManager.addCustomInputGestureErrorCode =
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.addCustomInputGesture(any()))
- .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE)
openAddShortcutDialogAndSetShortcut()
@@ -202,11 +233,12 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
}
@Test
- fun uiState_errorMessage_isGenericError_whenErrorIsUnknown() {
+ fun uiState_errorMessage_onSetShortcut_isGenericError_whenErrorIsUnknown() {
testScope.runTest {
+ inputManager.addCustomInputGesture(buildSimpleInputGestureWithMetaCtrlATrigger())
+ kosmos.fakeInputManager.addCustomInputGestureErrorCode =
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.addCustomInputGesture(any()))
- .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER)
openAddShortcutDialogAndSetShortcut()
@@ -219,10 +251,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
fun uiState_becomesInactiveAfterSuccessfullyDeletingShortcut() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.getCustomInputGestures(any()))
- .thenReturn(listOf(goHomeInputGestureData, allAppsInputGestureData))
- whenever(inputManager.removeCustomInputGesture(any()))
- .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS)
+ inputManager.addCustomInputGesture(allAppsInputGestureData)
openDeleteShortcutDialogAndDeleteShortcut()
@@ -234,7 +263,6 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
fun uiState_becomesInactiveAfterSuccessfullyResettingShortcuts() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- whenever(inputManager.getCustomInputGestures(any())).thenReturn(emptyList())
openResetShortcutDialogAndResetAllCustomShortcuts()
@@ -297,24 +325,42 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
}
}
+ @Test
+ fun uiState_pressedKeys_resetsToEmpty_onClearSelectedShortcutKeyCombination() {
+ testScope.runTest {
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+ viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
+ viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
+ viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
+ viewModel.clearSelectedKeyCombination()
+ assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty()
+ }
+ }
+
private suspend fun openAddShortcutDialogAndSetShortcut() {
- viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
+ openAddShortcutDialogAndPressKeyCombination()
+ viewModel.onSetShortcut()
+ }
+ private fun openAddShortcutDialogAndPressKeyCombination() {
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
-
- viewModel.onSetShortcut()
}
private suspend fun openDeleteShortcutDialogAndDeleteShortcut() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
-
viewModel.deleteShortcutCurrentlyBeingCustomized()
}
private suspend fun openResetShortcutDialogAndResetAllCustomShortcuts() {
viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
-
viewModel.resetAllCustomShortcuts()
}
+
+ private fun buildSimpleInputGestureWithMetaCtrlATrigger() =
+ InputGestureData.Builder()
+ .setKeyGestureType(KEY_GESTURE_TYPE_HOME)
+ .setTrigger(createKeyTrigger(KEYCODE_A, META_CTRL_ON or META_META_ON))
+ .build()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index c8a16483a00c..abcbdb153e80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -27,6 +27,8 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
+import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
@@ -34,6 +36,8 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
@@ -153,6 +157,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
if (!SceneContainerFlag.isEnabled) {
mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
}
+ if (glanceableHubV2()) {
+ kosmos.setCommunalV2ConfigEnabled(true)
+ }
featureFlags = FakeFeatureFlags()
fromLockscreenTransitionInteractor.start()
@@ -1948,6 +1955,39 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
@Test
@DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
+ fun glanceableHubToDreaming_v2() =
+ testScope.runTest {
+ kosmos.setCommunalV2Enabled(true)
+
+ // GIVEN a device that is not dreaming or dozing
+ keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ advanceTimeBy(600.milliseconds)
+
+ // GIVEN a prior transition has run to glanceable hub
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceTimeBy(100.milliseconds)
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ animatorAssertion = { it.isNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ @DisableSceneContainer
@DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToDreaming() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 9d5bf4dbdc3f..a276f514b779 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -1045,6 +1045,41 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertThat(usingKeyguardGoingAwayAnimation).isFalse()
}
+ @Test
+ fun aodVisibility_visibleFullyInAod_falseOtherwise() =
+ testScope.runTest {
+ val aodVisibility by collectValues(underTest.value.aodVisibility)
+
+ transitionRepository.sendTransitionStepsThroughRunning(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope,
+ throughValue = 0.5f,
+ )
+
+ assertEquals(listOf(false), aodVisibility)
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(listOf(false, true), aodVisibility)
+
+ transitionRepository.sendTransitionStepsThroughRunning(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ runCurrent()
+
+ assertEquals(listOf(false, true, false), aodVisibility)
+ }
+
companion object {
private val progress = MutableStateFlow(0f)
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 286f8bfd63a2..e93ed39274fb 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
@@ -20,6 +20,7 @@ import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.BrokenWithSceneContainer
@@ -97,6 +98,7 @@ class AlternateBouncerToPrimaryBouncerTransitionViewModelTest : SysuiTestCase()
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
fun blurRadiusGoesToMaximumWhenShadeIsExpanded() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
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 60a19a4c7d07..aaca603ecf77 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
@@ -21,6 +21,7 @@ import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -155,6 +156,7 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
@BrokenWithSceneContainer(388068805)
fun blurRadiusIsMaxWhenShadeIsExpanded() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
index 6db876756d3a..0951df24c56f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,50 +16,96 @@
package com.android.systemui.keyguard.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.andSceneContainer
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.kosmos.testScope
import com.android.systemui.testKosmos
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class OccludedToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class OccludedToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization) :
+ SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val underTest by lazy { kosmos.occludedToPrimaryBouncerTransitionViewModel }
+ private lateinit var underTest: OccludedToPrimaryBouncerTransitionViewModel
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setup() {
+ underTest = kosmos.occludedToPrimaryBouncerTransitionViewModel
+ }
@Test
- @DisableSceneContainer
- fun blurBecomesMaxValueImmediately() =
+ @BrokenWithSceneContainer(388068805)
+ fun notificationsAreBlurredImmediatelyWhenBouncerIsOpenedAndShadeIsExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.notificationBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(true)
+
+ kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = kosmos.blurConfig.maxBlurRadiusPx,
+ endValue = kosmos.blurConfig.maxBlurRadiusPx,
+ actualValuesProvider = { values },
+ transitionFactory = ::step,
+ checkInterpolatedValues = false,
+ )
+ }
+
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
+ @BrokenWithSceneContainer(388068805)
+ fun blurBecomesMaxValueImmediatelyWhenShadeIsAlreadyExpanded() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(true)
kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
startValue = kosmos.blurConfig.maxBlurRadiusPx,
endValue = kosmos.blurConfig.maxBlurRadiusPx,
actualValuesProvider = { values },
- transitionFactory = { step, transitionState ->
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = step,
- transitionState = transitionState,
- ownerName = "OccludedToPrimaryBouncerTransitionViewModelTest",
- )
- },
+ transitionFactory = ::step,
checkInterpolatedValues = false,
)
}
+
+ fun step(value: Float, state: TransitionState = TransitionState.RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = state,
+ ownerName = "OccludedToPrimaryBouncerTransitionViewModelTest",
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 0db0c5fe8482..8fefb8d40b71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -16,12 +16,16 @@
package com.android.systemui.keyguard.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -35,13 +39,17 @@ 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
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class PrimaryBouncerToLockscreenTransitionViewModelTest(flags: FlagsParameterization) :
+ SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
@@ -49,9 +57,27 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
val biometricSettingsRepository = kosmos.biometricSettingsRepository
- val underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
+ private lateinit var underTest: PrimaryBouncerToLockscreenTransitionViewModel
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setup() {
+ underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
+ }
@Test
+ @BrokenWithSceneContainer(392346450)
fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
testScope.runTest {
val viewState = ViewStateAccessor(alpha = { 0.5f })
@@ -70,6 +96,7 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @BrokenWithSceneContainer(392346450)
fun deviceEntryParentViewAlpha() =
testScope.runTest {
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
@@ -89,6 +116,7 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @BrokenWithSceneContainer(392346450)
fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() =
testScope.runTest {
fingerprintPropertyRepository.supportsUdfps()
@@ -113,6 +141,7 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @BrokenWithSceneContainer(388068805)
fun blurRadiusGoesFromMaxToMinWhenShadeIsNotExpanded() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
@@ -128,6 +157,8 @@ class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_SHADE_BLUR)
+ @BrokenWithSceneContainer(388068805)
fun blurRadiusRemainsAtMaxWhenShadeIsExpanded() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
index b0b4af5fea5b..fd7fb9f863c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelTest.kt
@@ -16,11 +16,13 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
@@ -40,10 +42,37 @@ class PrimaryBouncerToOccludedTransitionViewModelTest : SysuiTestCase() {
private val underTest by lazy { kosmos.primaryBouncerToOccludedTransitionViewModel }
@Test
- @DisableSceneContainer
- fun blurBecomesMaxValueImmediately() =
+ @BrokenWithSceneContainer(388068805)
+ fun blurBecomesMinValueImmediatelyWhenShadeIsNotExpanded() =
testScope.runTest {
val values by collectValues(underTest.windowBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(false)
+
+ kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
+ transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
+ startValue = kosmos.blurConfig.minBlurRadiusPx,
+ endValue = kosmos.blurConfig.minBlurRadiusPx,
+ actualValuesProvider = { values },
+ transitionFactory = { step, transitionState ->
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.OCCLUDED,
+ value = step,
+ transitionState = transitionState,
+ ownerName = "PrimaryBouncerToOccludedTransitionViewModelTest",
+ )
+ },
+ checkInterpolatedValues = false,
+ )
+ }
+
+ @Test
+ @BrokenWithSceneContainer(388068805)
+ @EnableFlags(Flags.FLAG_NOTIFICATION_SHADE_BLUR)
+ fun blurBecomesMaxValueImmediatelyWhenShadeIsExpanded() =
+ testScope.runTest {
+ val values by collectValues(underTest.windowBlurRadius)
+ kosmos.keyguardWindowBlurTestUtil.shadeExpanded(false)
kosmos.keyguardWindowBlurTestUtil.assertTransitionToBlurRadius(
transitionProgress = listOf(0.0f, 0.2f, 0.3f, 0.65f, 0.7f, 1.0f),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 63ec78fd9ee5..6db5fb4f7d7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -265,7 +265,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getContentDescription()).isNotNull();
- assertThat(mViewHolder.mSeekBar.getAccessibilityDelegate()).isNotNull();
assertThat(mViewHolder.mContainerLayout.isFocusable()).isFalse();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS
new file mode 100644
index 000000000000..739d2ac2e87b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/OWNERS
@@ -0,0 +1 @@
+file:/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index a770ee199ba6..c1872f05aa1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -57,7 +57,7 @@ import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
@@ -104,7 +104,7 @@ public class NavBarHelperTest extends SysuiTestCase {
@Mock
SystemActions mSystemActions;
@Mock
- OverviewProxyService mOverviewProxyService;
+ LauncherProxyService mLauncherProxyService;
@Mock
Lazy<AssistManager> mAssistManagerLazy;
@Mock
@@ -161,7 +161,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mAccessibilityGestureTargetObserver,
- mSystemActions, mOverviewProxyService, mAssistManagerLazy,
+ mSystemActions, mLauncherProxyService, mAssistManagerLazy,
() -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
@@ -171,7 +171,7 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void registerListenersInCtor() {
verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
- verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ verify(mLauncherProxyService, times(1)).addCallback(mNavBarHelper);
verify(mCommandQueue, times(1)).addCallback(any());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index 9bae7bd72f7d..cf0a25020d93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -9,7 +9,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.LauncherProxyService
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.shared.system.TaskStackChangeListeners
@@ -49,7 +49,7 @@ class TaskbarDelegateTest : SysuiTestCase() {
@Mock lateinit var mLightBarControllerFactory: LightBarTransitionsController.Factory
@Mock lateinit var mLightBarTransitionController: LightBarTransitionsController
@Mock lateinit var mCommandQueue: CommandQueue
- @Mock lateinit var mOverviewProxyService: OverviewProxyService
+ @Mock lateinit var mLauncherProxyService: LauncherProxyService
@Mock lateinit var mNavBarHelper: NavBarHelper
@Mock lateinit var mNavigationModeController: NavigationModeController
@Mock lateinit var mSysUiState: SysUiState
@@ -87,7 +87,7 @@ class TaskbarDelegateTest : SysuiTestCase() {
)
mTaskbarDelegate.setDependencies(
mCommandQueue,
- mOverviewProxyService,
+ mLauncherProxyService,
mNavBarHelper,
mNavigationModeController,
mSysUiState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
index 00e79f5a3ac2..fd4bb4bb8a37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
@@ -40,7 +40,7 @@ import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -80,7 +80,7 @@ public class NavigationBarButtonTest extends SysuiTestCase {
.thenReturn(mEdgeBackGestureHandler);
mDependency.injectMockDependency(AssistManager.class);
- mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(LauncherProxyService.class);
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
index e58c8f281fc1..85c093c16d88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarInflaterViewTest.java
@@ -35,7 +35,7 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.views.buttons.ButtonDispatcher;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import org.junit.After;
import org.junit.Before;
@@ -55,7 +55,7 @@ public class NavigationBarInflaterViewTest extends SysuiTestCase {
@Before
public void setUp() {
mDependency.injectMockDependency(AssistManager.class);
- mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(LauncherProxyService.class);
mDependency.injectMockDependency(NavigationModeController.class);
mDependency.injectMockDependency(NavigationBarController.class);
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 b7845b73b416..09e49eb217b0 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
@@ -106,7 +106,7 @@ import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
import com.android.systemui.navigationbar.views.buttons.NavBarButtonClickLogger;
import com.android.systemui.navigationbar.views.buttons.NavbarOrientationTrackingLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -185,7 +185,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private SystemActions mSystemActions;
@Mock
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -285,14 +285,14 @@ public class NavigationBarTest extends SysuiTestCase {
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
mDependency.injectMockDependency(NavigationBarController.class);
- mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
+ mDependency.injectTestDependency(LauncherProxyService.class, mLauncherProxyService);
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
mock(AccessibilityButtonModeObserver.class),
mock(AccessibilityButtonTargetsObserver.class),
mock(AccessibilityGestureTargetsObserver.class),
- mSystemActions, mOverviewProxyService,
+ mSystemActions, mLauncherProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
mKeyguardStateController, mock(NavigationModeController.class),
mEdgeBackGestureHandlerFactory, mock(IWindowManager.class),
@@ -690,7 +690,7 @@ public class NavigationBarTest extends SysuiTestCase {
mock(AccessibilityManager.class),
deviceProvisionedController,
new MetricsLogger(),
- mOverviewProxyService,
+ mLauncherProxyService,
mNavigationModeController,
mStatusBarStateController,
mStatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
index 3621ab975daf..cff9beccc729 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTransitionsTest.java
@@ -36,7 +36,7 @@ import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -72,7 +72,7 @@ public class NavigationBarTransitionsTest extends SysuiTestCase {
when(mEdgeBackGestureHandlerFactory.create(any(Context.class)))
.thenReturn(mEdgeBackGestureHandler);
mDependency.injectMockDependency(AssistManager.class);
- mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(LauncherProxyService.class);
mDependency.injectMockDependency(StatusBarStateController.class);
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
index 403a883e1760..58ec0c7a0e72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/buttons/KeyButtonViewTest.java
@@ -51,7 +51,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import org.junit.Before;
import org.junit.Test;
@@ -76,7 +76,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(LauncherProxyService.class);
mDependency.injectMockDependency(AssistManager.class);
mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 855931c32671..52b9e47e6d3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -53,7 +55,7 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+ assertThat((actions?.get(Swipe.Up) as? HideOverlay)?.overlay)
.isEqualTo(Overlays.NotificationsShade)
assertThat(actions?.get(Swipe.Down)).isNull()
}
@@ -64,7 +66,7 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+ assertThat((actions?.get(Back) as? HideOverlay)?.overlay)
.isEqualTo(Overlays.NotificationsShade)
}
@@ -74,11 +76,11 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- assertThat(
- (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight))
- as? UserActionResult.ReplaceByOverlay)
- ?.overlay
- )
- .isEqualTo(Overlays.QuickSettingsShade)
+ val action =
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight)) as? ShowOverlay)
+ assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade)
+ val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
+ assertThat(overlaysToHide).isNotNull()
+ assertThat(overlaysToHide?.overlays).containsExactly(Overlays.NotificationsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index e9633f49f76d..ff005c2b767a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs;
-import static com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS;
import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
import static com.google.common.truth.Truth.assertThat;
@@ -41,8 +40,6 @@ import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper.RunWithLooper;
import android.view.ContextThemeWrapper;
@@ -87,6 +84,7 @@ import javax.inject.Provider;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlow;
+
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -505,7 +503,6 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
}
@Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
public void setTiles_longPressEffectEnabled_nonNullLongPressEffectsAreProvided() {
mLongPressEffectProvider.mEffectsProvided = 0;
when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
@@ -516,16 +513,6 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
- public void setTiles_longPressEffectDisabled_noLongPressEffectsAreProvided() {
- mLongPressEffectProvider.mEffectsProvided = 0;
- when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
- mController.setTiles();
-
- assertThat(mLongPressEffectProvider.mEffectsProvided).isEqualTo(0);
- }
-
- @Test
public void setTiles_differentTiles_extraTileRemoved() {
when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
mController.setTiles();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index fecd8c3cacca..4c834b396df6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -37,6 +37,7 @@ import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
import com.android.systemui.res.R
@@ -109,6 +110,7 @@ class InternetTileNewImplTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var dialogManager: InternetDialogManager
@Mock private lateinit var wifiStateWorker: WifiStateWorker
@Mock private lateinit var accessPointController: AccessPointController
+ @Mock private lateinit var internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
@Before
fun setUp() {
@@ -145,6 +147,7 @@ class InternetTileNewImplTest(flags: FlagsParameterization) : SysuiTestCase() {
dialogManager,
wifiStateWorker,
accessPointController,
+ internetDetailsViewModelFactory
)
underTest.initialize()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index e4a988860a6e..ce4a3432a5b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -26,13 +26,13 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.dialog.InternetDetailsContentManager
+import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.dialog.WifiStateWorker
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.statusbar.connectivity.AccessPointController
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,9 +40,10 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@SmallTest
@@ -54,15 +55,27 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
private lateinit var underTest: InternetTileUserActionInteractor
- @Mock private lateinit var internetDialogManager: InternetDialogManager
- @Mock private lateinit var wifiStateWorker: WifiStateWorker
- @Mock private lateinit var controller: AccessPointController
+ private lateinit var internetDialogManager: InternetDialogManager
+ private lateinit var wifiStateWorker: WifiStateWorker
+ private lateinit var controller: AccessPointController
+ private lateinit var internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+ private lateinit var internetDetailsContentManagerFactory: InternetDetailsContentManager.Factory
+ private lateinit var internetDetailsViewModel: InternetDetailsViewModel
@Before
fun setup() {
internetDialogManager = mock<InternetDialogManager>()
wifiStateWorker = mock<WifiStateWorker>()
controller = mock<AccessPointController>()
+ internetDetailsViewModelFactory = mock<InternetDetailsViewModel.Factory>()
+ internetDetailsContentManagerFactory = mock<InternetDetailsContentManager.Factory>()
+ internetDetailsViewModel =
+ InternetDetailsViewModel(
+ onLongClick = {},
+ accessPointController = mock<AccessPointController>(),
+ contentManagerFactory = internetDetailsContentManagerFactory,
+ )
+ whenever(internetDetailsViewModelFactory.create(any())).thenReturn(internetDetailsViewModel)
underTest =
InternetTileUserActionInteractor(
@@ -71,6 +84,7 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
wifiStateWorker,
controller,
inputHandler,
+ internetDetailsViewModelFactory,
)
}
@@ -102,7 +116,7 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(QSTileInputTestKtx.longClick(input))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
- Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
}
}
@@ -114,7 +128,7 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(QSTileInputTestKtx.longClick(input))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
- Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
}
}
@@ -141,8 +155,7 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() {
@Test
fun detailsViewModel() =
kosmos.testScope.runTest {
- assertThat(underTest.detailsViewModel.getTitle())
- .isEqualTo("Internet")
+ assertThat(underTest.detailsViewModel.getTitle()).isEqualTo("Internet")
assertThat(underTest.detailsViewModel.getSubTitle())
.isEqualTo("Tab a network to connect")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index 939644594d31..df2dd99c779e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -53,7 +55,7 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+ assertThat((actions?.get(Swipe.Up) as? HideOverlay)?.overlay)
.isEqualTo(Overlays.QuickSettingsShade)
assertThat(actions?.get(Swipe.Down)).isNull()
}
@@ -66,7 +68,7 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
assertThat(isEditing).isFalse()
- assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+ assertThat((actions?.get(Back) as? HideOverlay)?.overlay)
.isEqualTo(Overlays.QuickSettingsShade)
}
@@ -87,11 +89,11 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
val actions by collectLastValue(underTest.actions)
underTest.activateIn(this)
- assertThat(
- (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft))
- as? UserActionResult.ReplaceByOverlay)
- ?.overlay
- )
- .isEqualTo(Overlays.NotificationsShade)
+ val action =
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft)) as? ShowOverlay)
+ assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade)
+ val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
+ assertThat(overlaysToHide).isNotNull()
+ assertThat(overlaysToHide?.overlays).containsExactly(Overlays.QuickSettingsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 0713a247a4a3..baaf6c9a76ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -84,7 +84,7 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.log.LogWtfHandlerRule;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -156,7 +156,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
@Mock
private NotificationClickNotifier mClickNotifier;
@Mock
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
@Mock
private KeyguardManager mKeyguardManager;
@Mock
@@ -1142,7 +1142,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
(() -> mVisibilityProvider),
(() -> mNotifCollection),
mClickNotifier,
- (() -> mOverviewProxyService),
+ (() -> mLauncherProxyService),
NotificationLockscreenUserManagerTest.this.mKeyguardManager,
mStatusBarStateController,
mMainExecutor,
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 55f3717535b7..942e6554e5d9 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
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifCh
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
@@ -44,6 +45,7 @@ import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -629,22 +631,26 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
- fun chips_clickingChipNotifiesInteractor() =
+ @DisableFlags(
+ FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY,
+ StatusBarRootModernization.FLAG_NAME,
+ StatusBarChipsModernization.FLAG_NAME,
+ )
+ fun chips_chipsModernizationDisabled_clickingChipNotifiesInteractor() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
- val latestChipTap by
+ val latestChipTapKey by
collectLastValue(
kosmos.statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent
)
+ val key = "clickTest"
setNotifs(
listOf(
activeNotificationModel(
- key = "clickTest",
+ key,
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("clickTest").build(),
+ promotedContent = PromotedNotificationContentModel.Builder(key).build(),
)
)
)
@@ -652,7 +658,41 @@ class NotifChipsViewModelTest : SysuiTestCase() {
chip.onClickListenerLegacy!!.onClick(mock<View>())
- assertThat(latestChipTap).isEqualTo("clickTest")
+ assertThat(latestChipTapKey).isEqualTo(key)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
+ fun chips_chipsModernizationEnabled_clickingChipNotifiesInteractor() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+ val latestChipTapKey by
+ collectLastValue(
+ kosmos.statusBarNotificationChipsInteractor.promotedNotificationChipTapEvent
+ )
+ val key = "clickTest"
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+ )
+ )
+ )
+ val chip = latest!![0]
+
+ assertThat(chip.clickBehavior)
+ .isInstanceOf(
+ OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification::class.java
+ )
+
+ (chip.clickBehavior as OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification)
+ .onClick()
+
+ assertThat(latestChipTapKey).isEqualTo(key)
}
private fun setNotifs(notifs: List<ActiveNotificationModel>) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 3c772fdbe0b2..356eedbc9a45 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
+import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -242,4 +243,42 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
// Then: need no re-inflation
assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
}
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI)
+ fun changeIsSummarization_needReInflation_newlySummarized() {
+ // Given: an Entry with no summarization
+ val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(oldAdjustment.summarization).isNull()
+
+ // When: the Entry now has a summarization
+ val rb = RankingBuilder(entry.ranking)
+ rb.setSummarization("summary!")
+ entry.ranking = rb.build()
+ val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+ // Then: Need re-inflation
+ assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI)
+ fun changeIsSummarization_needReInflation_summarizationChanged() {
+ // Given: an Entry with no summarization
+ val rb = RankingBuilder(entry.ranking)
+ rb.setSummarization("summary!")
+ entry.ranking = rb.build()
+ val oldAdjustment = adjustmentProvider.calculateAdjustment(entry)
+
+ // When: the Entry now has a new summarization
+ val rb2 = RankingBuilder(entry.ranking)
+ rb2.setSummarization("summary new!")
+ entry.ranking = rb2.build()
+ val newAdjustment = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(newAdjustment).isNotEqualTo(oldAdjustment)
+
+ // Then: Need re-inflation
+ assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment))
+ }
}
diff --git a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
index f7dac13888c0..ea494b4642d5 100644
--- a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
@@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyguard_lock_padding"
- android:focusable="true"
+ android:focusable="false"
/>
<com.android.keyguard.BouncerKeyguardMessageArea
@@ -30,6 +30,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/secondary_message_padding"
- android:focusable="true" />
+ android:focusable="false" />
</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 2a8f1b596711..f231df2f1a10 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -66,7 +66,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 76f6f599c54c..04457229d573 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -31,7 +31,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
index 5879c110d8a1..b184344f2f24 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -67,7 +67,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 3f7b02835357..0e15ff66f3ee 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -35,7 +35,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index b464fb3bafed..f6ac02aee657 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -74,7 +74,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 21580731aed2..ba4da794d777 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -32,7 +32,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
index cc99f5e125f3..dd5f7e4e2ed4 100644
--- a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
+++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
@@ -30,7 +30,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
+ android:textAppearance="@style/TextAppearance.QS.Status"
android:layout_marginEnd="@dimen/qs_carrier_margin_width"
android:visibility="gone"
android:textDirection="locale"
diff --git a/packages/SystemUI/res/layout/low_light_clock_dream.xml b/packages/SystemUI/res/layout/low_light_clock_dream.xml
new file mode 100644
index 000000000000..3d74a9fd8ae3
--- /dev/null
+++ b/packages/SystemUI/res/layout/low_light_clock_dream.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/low_light_clock_dream"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/low_light_clock_background_color">
+
+ <TextClock
+ android:id="@+id/low_light_text_clock"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/low_light_clock_text_size"
+ android:layout_gravity="center"
+ android:fontFamily="google-sans-clock"
+ android:gravity="center_horizontal"
+ android:textColor="@color/low_light_clock_text_color"
+ android:autoSizeTextType="uniform"
+ android:autoSizeMaxTextSize="@dimen/low_light_clock_text_size"
+ android:format12Hour="h:mm"
+ android:format24Hour="H:mm"/>
+
+ <TextView
+ android:id="@+id/charging_status_text_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
+ android:gravity="center"
+ android:minHeight="@dimen/low_light_clock_charging_text_min_height"
+ android:layout_gravity="center_horizontal|bottom"
+ android:paddingStart="@dimen/keyguard_indication_text_padding"
+ android:paddingEnd="@dimen/keyguard_indication_text_padding"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:textSize="@dimen/low_light_clock_charging_text_size"
+ android:textFontWeight="@integer/low_light_clock_charging_text_font_weight"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:accessibilityLiveRegion="polite" />
+ </FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 21e0d2c0b8d7..f943f8660d65 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -52,7 +52,6 @@
android:layout_height="64dp"
android:focusable="false"
android:importantForAccessibility="no"
- android:background="@drawable/media_output_title_icon_area"
android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/title_icon"
diff --git a/packages/SystemUI/res/layout/shade_carrier.xml b/packages/SystemUI/res/layout/shade_carrier.xml
index 0fed393a7ed3..6a5df9c3ed10 100644
--- a/packages/SystemUI/res/layout/shade_carrier.xml
+++ b/packages/SystemUI/res/layout/shade_carrier.xml
@@ -33,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textAppearance="@style/TextAppearance.QS.Status.Carriers"
+ android:textAppearance="@style/TextAppearance.QS.Status"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/shade_carrier_group.xml b/packages/SystemUI/res/layout/shade_carrier_group.xml
index 2e8f98cbd190..6551f3b8160d 100644
--- a/packages/SystemUI/res/layout/shade_carrier_group.xml
+++ b/packages/SystemUI/res/layout/shade_carrier_group.xml
@@ -32,7 +32,7 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.QS.Status.Carriers.NoCarrierText"
+ android:textAppearance="@style/TextAppearance.QS.Status"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 36ede64f91d9..015e0e83e57d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -24,6 +24,7 @@
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
<color name="status_bar_clock_color">#FFFFFFFF</color>
+ <color name="shade_header_text_color">#FFFFFFFF</color>
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
<color name="status_bar_icons_hover_color_light">#38FFFFFF</color> <!-- 22% white -->
<color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black -->
@@ -260,4 +261,8 @@
<!-- Rear Display Education -->
<color name="rear_display_overlay_animation_background_color">#1E1B17</color>
<color name="rear_display_overlay_dialog_background_color">#1E1B17</color>
+
+ <!-- Low light Dream -->
+ <color name="low_light_clock_background_color">#000000</color>
+ <color name="low_light_clock_text_color">#CCCCCC</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 724a12c12490..c7f037f3d619 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1614,6 +1614,18 @@
<!-- GLANCEABLE_HUB -> DREAMING transition: Amount to shift dream overlay on entering -->
<dimen name="hub_to_dreaming_transition_dream_overlay_translation_x">824dp</dimen>
+ <!-- Low light clock -->
+ <!-- The text size of the low light clock is intentionally defined in dp to avoid scaling -->
+ <dimen name="low_light_clock_text_size">260dp</dimen>
+ <dimen name="low_light_clock_charging_text_size">14sp</dimen>
+ <dimen name="low_light_clock_charging_text_min_height">48dp</dimen>
+ <integer name="low_light_clock_charging_text_font_weight">500</integer>
+
+ <dimen name="low_light_clock_translate_animation_offset">40dp</dimen>
+ <integer name="low_light_clock_translate_animation_duration_ms">1167</integer>
+ <integer name="low_light_clock_alpha_animation_in_start_delay_ms">233</integer>
+ <integer name="low_light_clock_alpha_animation_duration_ms">250</integer>
+
<!-- Distance that the full shade transition takes in order for media to fully transition to
the shade -->
<dimen name="lockscreen_shade_media_transition_distance">120dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c95c6dd89353..fa6a41a74ca9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -176,17 +176,11 @@
<style name="TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?attr/onSurface</item>
+ <item name="android:textColor">@color/shade_header_text_color</item>
<item name="android:textSize">14sp</item>
<item name="android:letterSpacing">0.01</item>
</style>
- <style name="TextAppearance.QS.Status.Carriers" />
-
- <style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
- <item name="android:textColor">?attr/onSurfaceVariant</item>
- </style>
-
<style name="TextAppearance.QS.Status.Build">
<item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
index 011458859db4..b43ffc530289 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
@@ -24,7 +24,7 @@ import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
// Next ID: 39
-oneway interface IOverviewProxy {
+oneway interface ILauncherProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -140,7 +140,7 @@ oneway interface IOverviewProxy {
void appTransitionPending(boolean pending) = 34;
/**
- * Sent right after OverviewProxy calls unbindService() on the TouchInteractionService.
+ * Sent right after LauncherProxyService calls unbindService() on the TouchInteractionService.
* TouchInteractionService is expected to send the reply once it has finished cleaning up.
*/
void onUnbind(IRemoteCallback reply) = 35;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e332280bc31a..1f6bea18d53a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -69,10 +69,10 @@ interface ISystemUiProxy {
/**
* Indicates that the given Assist invocation types should be handled by Launcher via
- * OverviewProxy#onAssistantOverrideInvoked and should not be invoked by SystemUI.
+ * LauncherProxy#onAssistantOverrideInvoked and should not be invoked by SystemUI.
*
* @param invocationTypes The invocation types that will henceforth be handled via
- * OverviewProxy (Launcher); other invocation types should be handled by SysUI.
+ * LauncherProxy (Launcher); other invocation types should be handled by SysUI.
*/
oneway void setAssistantOverridesRequested(in int[] invocationTypes) = 53;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 7f176de547bc..0e9d8fec9717 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -164,6 +166,8 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
layoutParams.height = (int) getResources().getDimension(
R.dimen.keyguard_pin_field_height);
}
+
+ mPasswordEntry.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
}
private void setKeyboardBasedFocusOutline(boolean isAnyKeyboardConnected) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 40c1f0f9895d..4a8e4ed3f6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -39,7 +39,7 @@ import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -129,7 +129,7 @@ public class Dependency {
@Inject Lazy<MetricsLogger> mMetricsLogger;
@Inject Lazy<UiOffloadThread> mUiOffloadThread;
@Inject Lazy<LightBarController> mLightBarController;
- @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+ @Inject Lazy<LauncherProxyService> mLauncherProxyService;
@Inject Lazy<NavigationModeController> mNavBarModeController;
@Inject Lazy<NavigationBarController> mNavigationBarController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@@ -175,7 +175,7 @@ public class Dependency {
mProviders.put(MetricsLogger.class, mMetricsLogger::get);
mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
mProviders.put(LightBarController.class, mLightBarController::get);
- mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
+ mProviders.put(LauncherProxyService.class, mLauncherProxyService::get);
mProviders.put(NavigationModeController.class, mNavBarModeController::get);
mProviders.put(NavigationBarController.class, mNavigationBarController::get);
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 5cba464fc24c..5482c3d3ea18 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -50,7 +50,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -79,7 +79,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final Executor mExecutor;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final DisplayTracker mDisplayTracker;
private final AccessibilityLogger mA11yLogger;
@@ -225,13 +225,13 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
public MagnificationImpl(Context context,
@Main Handler mainHandler, @Main Executor executor,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SysUiState sysUiState, LauncherProxyService launcherProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager, AccessibilityManager accessibilityManager,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
this(context, mainHandler.getLooper(), executor, commandQueue,
- modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
+ modeSwitchesController, sysUiState, launcherProxyService, secureSettings,
displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager,
viewCaptureAwareWindowManager);
}
@@ -239,7 +239,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
@VisibleForTesting
public MagnificationImpl(Context context, Looper looper, @Main Executor executor,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SysUiState sysUiState, LauncherProxyService launcherProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager,
@@ -258,7 +258,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
mSysUiState = sysUiState;
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mDisplayTracker = displayTracker;
mA11yLogger = a11yLogger;
mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
@@ -279,7 +279,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
@Override
public void start() {
mCommandQueue.addCallback(this);
- mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ mLauncherProxyService.addCallback(new LauncherProxyService.LauncherProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
if (isConnected) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 939d96e67f8f..da1c1bc49d23 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -37,7 +37,7 @@ import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -142,7 +142,7 @@ public class AssistManager {
protected final Context mContext;
private final AssistDisclosure mAssistDisclosure;
private final PhoneStateMonitor mPhoneStateMonitor;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final UiController mUiController;
protected final Lazy<SysUiState> mSysUiState;
protected final AssistLogger mAssistLogger;
@@ -176,7 +176,7 @@ public class AssistManager {
private final CommandQueue mCommandQueue;
protected final AssistUtils mAssistUtils;
- // Invocation types that should be sent over OverviewProxy instead of handled here.
+ // Invocation types that should be sent over LauncherProxy instead of handled here.
private int[] mAssistOverrideInvocationTypes;
@Inject
@@ -186,7 +186,7 @@ public class AssistManager {
AssistUtils assistUtils,
CommandQueue commandQueue,
PhoneStateMonitor phoneStateMonitor,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
Lazy<SysUiState> sysUiState,
DefaultUiController defaultUiController,
AssistLogger assistLogger,
@@ -203,7 +203,7 @@ public class AssistManager {
mCommandQueue = commandQueue;
mAssistUtils = assistUtils;
mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager);
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mPhoneStateMonitor = phoneStateMonitor;
mAssistLogger = assistLogger;
mUserTracker = userTracker;
@@ -220,7 +220,7 @@ public class AssistManager {
mSysUiState = sysUiState;
- mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ mLauncherProxyService.addCallback(new LauncherProxyService.LauncherProxyListener() {
@Override
public void onAssistantProgress(float progress) {
// Progress goes from 0 to 1 to indicate how close the assist gesture is to
@@ -288,14 +288,14 @@ public class AssistManager {
}
if (shouldOverrideAssist(args)) {
try {
- if (mOverviewProxyService.getProxy() == null) {
- Log.w(TAG, "No OverviewProxyService to invoke assistant override");
+ if (mLauncherProxyService.getProxy() == null) {
+ Log.w(TAG, "No LauncherProxyService to invoke assistant override");
return;
}
- mOverviewProxyService.getProxy().onAssistantOverrideInvoked(
+ mLauncherProxyService.getProxy().onAssistantOverrideInvoked(
args.getInt(INVOCATION_TYPE_KEY));
} catch (RemoteException e) {
- Log.w(TAG, "Unable to invoke assistant via OverviewProxyService override", e);
+ Log.w(TAG, "Unable to invoke assistant via LauncherProxyService override", e);
}
return;
}
@@ -333,7 +333,7 @@ public class AssistManager {
return shouldOverrideAssist(invocationType);
}
- /** @return true if the invocation type should be handled by OverviewProxy instead of SysUI. */
+ /** @return true if the invocation type should be handled by LauncherProxy instead of SysUI. */
public boolean shouldOverrideAssist(int invocationType) {
return mAssistOverrideInvocationTypes != null
&& Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
@@ -342,7 +342,7 @@ public class AssistManager {
/**
* @param invocationTypes The invocation types that will henceforth be handled via
- * OverviewProxy (Launcher); other invocation types should be handled by
+ * LauncherProxy (Launcher); other invocation types should be handled by
* this class.
*/
public void setAssistantOverridesRequested(int[] invocationTypes) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
new file mode 100644
index 000000000000..2e1b5ad177b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
@@ -0,0 +1,101 @@
+/*
+ * 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.communal;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition which estimates device inactivity in order to avoid launching a full-screen activity
+ * while the user is actively using the device.
+ */
+public class DeviceInactiveCondition extends Condition {
+ private final KeyguardStateController mKeyguardStateController;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ updateState();
+ }
+ };
+ private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onStartedGoingToSleep() {
+ updateState();
+ }
+ };
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ updateState();
+ }
+ };
+
+ @Inject
+ public DeviceInactiveCondition(@Application CoroutineScope scope,
+ KeyguardStateController keyguardStateController,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ super(scope);
+ mKeyguardStateController = keyguardStateController;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ protected void start() {
+ updateState();
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ }
+
+ @Override
+ protected void stop() {
+ mKeyguardStateController.removeCallback(mKeyguardStateCallback);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+ }
+
+ @Override
+ protected int getStartStrategy() {
+ return START_EAGERLY;
+ }
+
+ private void updateState() {
+ final boolean asleep =
+ mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
+ || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP;
+ updateCondition(asleep || mKeyguardStateController.isShowing()
+ || mKeyguardUpdateMonitor.isDreaming());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index c02784dfab1b..fe5a82cb5b8c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -81,6 +81,7 @@ import com.android.systemui.keyguard.ui.composable.LockscreenContent;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
+import com.android.systemui.lowlightclock.dagger.LowLightModule;
import com.android.systemui.mediaprojection.MediaProjectionModule;
import com.android.systemui.mediaprojection.appselector.MediaProjectionActivitiesModule;
import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule;
@@ -285,7 +286,8 @@ import javax.inject.Named;
UserModule.class,
UtilModule.class,
NoteTaskModule.class,
- WalletModule.class
+ WalletModule.class,
+ LowLightModule.class
},
subcomponents = {
ComplicationComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index faab31eff4f7..15f73ee0eda6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -48,6 +48,8 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
import com.android.systemui.res.R;
import com.android.systemui.touch.TouchInsetManager;
+import com.google.android.systemui.lowlightclock.LowLightClockDreamService;
+
import dagger.Binds;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -238,15 +240,24 @@ public interface DreamModule {
ComponentName bindsLowLightClockDream();
/**
+ * Provides low light clock dream service component.
+ */
+ @Provides
+ @Named(LOW_LIGHT_CLOCK_DREAM)
+ static ComponentName providesLowLightClockDream(Context context) {
+ return new ComponentName(context, LowLightClockDreamService.class);
+ }
+
+ /**
* Provides the component name of the low light dream, or null if not configured.
*/
@Provides
@Nullable
@Named(LOW_LIGHT_DREAM_SERVICE)
static ComponentName providesLowLightDreamService(Context context,
- @Named(LOW_LIGHT_CLOCK_DREAM) Optional<ComponentName> clockDream) {
- if (Flags.lowLightClockDream() && clockDream.isPresent()) {
- return clockDream.get();
+ @Named(LOW_LIGHT_CLOCK_DREAM) ComponentName clockDream) {
+ if (Flags.lowLightClockDream()) {
+ return clockDream;
}
String lowLightDreamComponent = context.getResources().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index d7a4dba3188a..9bdf812713d7 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -37,8 +37,8 @@ import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.time.Clock
import java.time.Instant
@@ -67,7 +67,7 @@ constructor(
private val contextualEducationInteractor: ContextualEducationInteractor,
private val userInputDeviceRepository: UserInputDeviceRepository,
private val tutorialRepository: TutorialSchedulerRepository,
- private val overviewProxyService: OverviewProxyService,
+ private val launcherProxyService: LauncherProxyService,
private val metricsLogger: ContextualEducationMetricsLogger,
@EduClock private val clock: Clock,
) : CoreStartable {
@@ -100,8 +100,8 @@ constructor(
val educationTriggered = _educationTriggered.asStateFlow()
private val statsUpdateRequests: Flow<StatsUpdateRequest> = conflatedCallbackFlow {
- val listener: OverviewProxyListener =
- object : OverviewProxyListener {
+ val listener: LauncherProxyListener =
+ object : LauncherProxyListener {
override fun updateContextualEduStats(
isTrackpadGesture: Boolean,
gestureType: GestureType,
@@ -113,8 +113,8 @@ constructor(
}
}
- overviewProxyService.addCallback(listener)
- awaitClose { overviewProxyService.removeCallback(listener) }
+ launcherProxyService.addCallback(listener)
+ awaitClose { launcherProxyService.removeCallback(listener) }
}
private val gestureModelMap: Flow<Map<GestureType, GestureEduModel>> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
index e5c638cbdfba..d355f761e5ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -32,18 +32,19 @@ import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestRe
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.SUCCESS
import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withContext
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
@SysUISingleton
class CustomInputGesturesRepository
@Inject
-constructor(private val userTracker: UserTracker,
- @Background private val bgCoroutineContext: CoroutineContext)
-{
+constructor(
+ private val userTracker: UserTracker,
+ @Background private val bgCoroutineContext: CoroutineContext,
+) {
private val userContext: Context
get() = userTracker.createCurrentUserContext(userTracker.userContext)
@@ -55,8 +56,7 @@ constructor(private val userTracker: UserTracker,
private val _customInputGesture = MutableStateFlow<List<InputGestureData>>(emptyList())
- val customInputGestures =
- _customInputGesture.onStart { refreshCustomInputGestures() }
+ val customInputGestures = _customInputGesture.onStart { refreshCustomInputGestures() }
fun refreshCustomInputGestures() {
setCustomInputGestures(inputGestures = retrieveCustomInputGestures())
@@ -72,24 +72,24 @@ constructor(private val userTracker: UserTracker,
} else emptyList()
}
- suspend fun addCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
+ suspend fun addCustomInputGesture(
+ inputGesture: InputGestureData
+ ): ShortcutCustomizationRequestResult {
return withContext(bgCoroutineContext) {
when (val result = inputManager.addCustomInputGesture(inputGesture)) {
CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
refreshCustomInputGestures()
SUCCESS
}
- CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS ->
- ERROR_RESERVED_COMBINATION
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS -> ERROR_RESERVED_COMBINATION
- CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE ->
- ERROR_RESERVED_COMBINATION
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE -> ERROR_RESERVED_COMBINATION
else -> {
Log.w(
TAG,
"Attempted to add inputGesture: $inputGesture " +
- "but ran into an error with code: $result",
+ "but ran into an error with code: $result",
)
ERROR_OTHER
}
@@ -97,11 +97,11 @@ constructor(private val userTracker: UserTracker,
}
}
- suspend fun deleteCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
- return withContext(bgCoroutineContext){
- when (
- val result = inputManager.removeCustomInputGesture(inputGesture)
- ) {
+ suspend fun deleteCustomInputGesture(
+ inputGesture: InputGestureData
+ ): ShortcutCustomizationRequestResult {
+ return withContext(bgCoroutineContext) {
+ when (val result = inputManager.removeCustomInputGesture(inputGesture)) {
CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
refreshCustomInputGestures()
SUCCESS
@@ -110,7 +110,7 @@ constructor(private val userTracker: UserTracker,
Log.w(
TAG,
"Attempted to delete inputGesture: $inputGesture " +
- "but ran into an error with code: $result",
+ "but ran into an error with code: $result",
)
ERROR_OTHER
}
@@ -134,7 +134,10 @@ constructor(private val userTracker: UserTracker,
}
}
+ suspend fun getInputGestureByTrigger(trigger: InputGestureData.Trigger): InputGestureData? =
+ withContext(bgCoroutineContext) { inputManager.getInputGesture(trigger) }
+
private companion object {
private const val TAG = "CustomInputGesturesRepository"
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 18ca877775df..6ae948d2da2e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyboard.shortcut.data.repository
import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.Builder
+import android.hardware.input.InputGestureData.Trigger
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent.KeyGestureType
@@ -175,6 +176,11 @@ constructor(
return customInputGesturesRepository.resetAllCustomInputGestures()
}
+ suspend fun isSelectedKeyCombinationAvailable(): Boolean {
+ val trigger = buildTriggerFromSelectedKeyCombination() ?: return false
+ return customInputGesturesRepository.getInputGestureByTrigger(trigger) == null
+ }
+
private fun Builder.addKeyGestureTypeForShortcutBeingCustomized(): Builder {
val keyGestureType = getKeyGestureTypeForShortcutBeingCustomized()
@@ -222,7 +228,10 @@ constructor(
)
}
- private fun Builder.addTriggerFromSelectedKeyCombination(): Builder {
+ private fun Builder.addTriggerFromSelectedKeyCombination(): Builder =
+ setTrigger(buildTriggerFromSelectedKeyCombination())
+
+ private fun buildTriggerFromSelectedKeyCombination(): Trigger? {
val selectedKeyCombination = _selectedKeyCombination.value
if (selectedKeyCombination?.keyCode == null) {
Log.w(
@@ -230,16 +239,14 @@ constructor(
"User requested to set shortcut but selected key combination is " +
"$selectedKeyCombination",
)
- return this
+ return null
}
- return setTrigger(
- createKeyTrigger(
- /* keycode = */ selectedKeyCombination.keyCode,
- /* modifierState = */ shortcutCategoriesUtils.removeUnsupportedModifiers(
- selectedKeyCombination.modifiers
- ),
- )
+ return createKeyTrigger(
+ /* keycode= */ selectedKeyCombination.keyCode,
+ /* modifierState= */ shortcutCategoriesUtils.removeUnsupportedModifiers(
+ selectedKeyCombination.modifiers
+ ),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
index ef242678a8ac..1a62517ad01d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
@@ -53,4 +53,7 @@ constructor(private val customShortcutRepository: CustomShortcutCategoriesReposi
suspend fun resetAllCustomShortcuts(): ShortcutCustomizationRequestResult {
return customShortcutRepository.resetAllCustomShortcuts()
}
+
+ suspend fun isSelectedKeyCombinationAvailable(): Boolean =
+ customShortcutRepository.isSelectedKeyCombinationAvailable()
}
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 bd3d46d09f5e..54e27a61ac78 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
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.create
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
class ShortcutCustomizationDialogStarter
@@ -57,20 +58,25 @@ constructor(
private val viewModel = viewModelFactory.create()
override suspend fun onActivated(): Nothing {
- viewModel.shortcutCustomizationUiState.collect { uiState ->
- when (uiState) {
- is AddShortcutDialog,
- is DeleteShortcutDialog,
- is ResetShortcutDialog -> {
- if (dialog == null) {
- dialog = createDialog().also { it.show() }
+ coroutineScope {
+ launch {
+ viewModel.shortcutCustomizationUiState.collect { uiState ->
+ when (uiState) {
+ is AddShortcutDialog,
+ is DeleteShortcutDialog,
+ is ResetShortcutDialog -> {
+ if (dialog == null) {
+ dialog = createDialog().also { it.show() }
+ }
+ }
+ is ShortcutCustomizationUiState.Inactive -> {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
- is ShortcutCustomizationUiState.Inactive -> {
- dialog?.dismiss()
- dialog = null
- }
}
+ launch { viewModel.activate() }
}
awaitCancellation()
}
@@ -101,6 +107,7 @@ constructor(
onConfirmResetShortcut = {
coroutineScope.launch { viewModel.resetAllCustomShortcuts() }
},
+ onClearSelectedKeyCombination = { viewModel.clearSelectedKeyCombination() },
)
setDialogProperties(dialog, uiState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
index fa03883e2a35..ea36a10fb01a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
@@ -24,7 +24,6 @@ import android.os.UserHandle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -36,6 +35,7 @@ import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelperBottomSheet
import com.android.systemui.keyboard.shortcut.ui.composable.getWidth
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
+import com.android.systemui.lifecycle.rememberActivated
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -51,14 +51,13 @@ class ShortcutHelperDialogStarter
constructor(
@Application private val applicationScope: CoroutineScope,
private val shortcutHelperViewModel: ShortcutHelperViewModel,
- shortcutCustomizationDialogStarterFactory: ShortcutCustomizationDialogStarter.Factory,
+ private val shortcutCustomizationDialogStarterFactory:
+ ShortcutCustomizationDialogStarter.Factory,
private val dialogFactory: SystemUIDialogFactory,
private val activityStarter: ActivityStarter,
) : CoreStartable {
@VisibleForTesting var dialog: Dialog? = null
- private val shortcutCustomizationDialogStarter =
- shortcutCustomizationDialogStarterFactory.create()
override fun start() {
shortcutHelperViewModel.shouldShow
@@ -77,7 +76,10 @@ constructor(
content = { dialog ->
val shortcutsUiState by
shortcutHelperViewModel.shortcutsUiState.collectAsStateWithLifecycle()
- LaunchedEffect(Unit) { shortcutCustomizationDialogStarter.activate() }
+ val shortcutCustomizationDialogStarter =
+ rememberActivated(traceName = "shortcutCustomizationDialogStarter") {
+ shortcutCustomizationDialogStarterFactory.create()
+ }
ShortcutHelper(
modifier = Modifier.width(getWidth()),
shortcutsUiState = shortcutsUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index 9d43c48ee274..66e45056989d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -18,14 +18,10 @@ package com.android.systemui.keyboard.shortcut.ui.composable
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
@@ -40,13 +36,15 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ErrorOutline
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
@@ -57,6 +55,7 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.LiveRegionMode
@@ -81,6 +80,7 @@ fun ShortcutCustomizationDialog(
onConfirmSetShortcut: () -> Unit,
onConfirmDeleteShortcut: () -> Unit,
onConfirmResetShortcut: () -> Unit,
+ onClearSelectedKeyCombination: () -> Unit,
) {
when (uiState) {
is ShortcutCustomizationUiState.AddShortcutDialog -> {
@@ -90,6 +90,7 @@ fun ShortcutCustomizationDialog(
onShortcutKeyCombinationSelected,
onCancel,
onConfirmSetShortcut,
+ onClearSelectedKeyCombination,
)
}
is ShortcutCustomizationUiState.DeleteShortcutDialog -> {
@@ -111,6 +112,7 @@ private fun AddShortcutDialog(
onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
onCancel: () -> Unit,
onConfirmSetShortcut: () -> Unit,
+ onClearSelectedKeyCombination: () -> Unit,
) {
Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
Title(uiState.shortcutLabel)
@@ -126,6 +128,7 @@ private fun AddShortcutDialog(
onShortcutKeyCombinationSelected = onShortcutKeyCombinationSelected,
pressedKeys = uiState.pressedKeys,
onConfirmSetShortcut = onConfirmSetShortcut,
+ onClearSelectedKeyCombination = onClearSelectedKeyCombination,
)
ErrorMessageContainer(uiState.errorMessage)
DialogButtons(
@@ -249,10 +252,11 @@ private fun ErrorMessageContainer(errorMessage: String) {
lineHeight = 20.sp,
fontWeight = FontWeight.W500,
color = MaterialTheme.colorScheme.error,
- modifier = Modifier.padding(start = 24.dp).width(252.dp).semantics {
- contentDescription = errorMessage
- liveRegion = LiveRegionMode.Polite
- },
+ modifier =
+ Modifier.padding(start = 24.dp).width(252.dp).semantics {
+ contentDescription = errorMessage
+ liveRegion = LiveRegionMode.Polite
+ },
)
}
}
@@ -264,72 +268,82 @@ private fun SelectedKeyCombinationContainer(
onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
pressedKeys: List<ShortcutKey>,
onConfirmSetShortcut: () -> Unit,
+ onClearSelectedKeyCombination: () -> Unit,
) {
- val interactionSource = remember { MutableInteractionSource() }
- val isFocused by interactionSource.collectIsFocusedAsState()
- val outlineColor =
- if (!isFocused) MaterialTheme.colorScheme.outline
- else if (shouldShowError) MaterialTheme.colorScheme.error
- else MaterialTheme.colorScheme.primary
val focusRequester = remember { FocusRequester() }
-
+ val focusManager = LocalFocusManager.current
LaunchedEffect(Unit) { focusRequester.requestFocus() }
- ClickableShortcutSurface(
- onClick = {},
- color = Color.Transparent,
- shape = RoundedCornerShape(50.dp),
+ OutlinedInputField(
modifier =
Modifier.padding(all = 16.dp)
.sizeIn(minWidth = 332.dp, minHeight = 56.dp)
- .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp))
+ .focusRequester(focusRequester)
+ .focusProperties { canFocus = true }
.onPreviewKeyEvent { keyEvent ->
val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent)
- if (
- !keyEventProcessed &&
- keyEvent.key == Key.Enter &&
- keyEvent.type == KeyEventType.KeyUp
- ) {
- onConfirmSetShortcut()
+ if (keyEventProcessed) {
true
- } else keyEventProcessed
- }
- .focusProperties { canFocus = true } // enables keyboard focus when in touch mode
- .focusRequester(focusRequester),
- interactionSource = interactionSource,
- ) {
- Row(
- modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 16.dp, bottom = 16.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- if (pressedKeys.isEmpty()) {
- PressKeyPrompt()
+ } else {
+ if (keyEvent.type == KeyEventType.KeyUp) {
+ when (keyEvent.key) {
+ Key.Enter -> {
+ onConfirmSetShortcut()
+ return@onPreviewKeyEvent true
+ }
+ Key.Backspace -> {
+ onClearSelectedKeyCombination()
+ return@onPreviewKeyEvent true
+ }
+ Key.DirectionDown -> {
+ focusManager.moveFocus(FocusDirection.Down)
+ return@onPreviewKeyEvent true
+ }
+ else -> return@onPreviewKeyEvent false
+ }
+ } else false
+ }
+ },
+ trailingIcon = { ErrorIcon(shouldShowError) },
+ isError = shouldShowError,
+ placeholder = { PressKeyPrompt() },
+ content =
+ if (pressedKeys.isNotEmpty()) {
+ { PressedKeysTextContainer(pressedKeys) }
} else {
- PressedKeysTextContainer(pressedKeys)
- }
- Spacer(modifier = Modifier.weight(1f))
- if (shouldShowError) {
- Icon(
- imageVector = Icons.Default.ErrorOutline,
- contentDescription = null,
- modifier = Modifier.size(20.dp),
- tint = MaterialTheme.colorScheme.error,
- )
- }
- }
+ null
+ },
+ )
+}
+
+@Composable
+private fun ErrorIcon(shouldShowError: Boolean) {
+ if (shouldShowError) {
+ Icon(
+ imageVector = Icons.Default.ErrorOutline,
+ contentDescription = null,
+ modifier = Modifier.size(20.dp),
+ tint = MaterialTheme.colorScheme.error,
+ )
}
}
@Composable
-private fun RowScope.PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) {
- pressedKeys.forEachIndexed { keyIndex, key ->
- if (keyIndex > 0) {
- ShortcutKeySeparator()
- }
- if (key is ShortcutKey.Text) {
- ShortcutTextKey(key)
- } else if (key is ShortcutKey.Icon) {
- ShortcutIconKey(key)
+private fun PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) {
+ Row(
+ modifier =
+ Modifier.semantics(mergeDescendants = true) { liveRegion = LiveRegionMode.Polite },
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ pressedKeys.forEachIndexed { keyIndex, key ->
+ if (keyIndex > 0) {
+ ShortcutKeySeparator()
+ }
+ if (key is ShortcutKey.Text) {
+ ShortcutTextKey(key)
+ } else if (key is ShortcutKey.Icon) {
+ ShortcutIconKey(key)
+ }
}
}
}
@@ -346,7 +360,7 @@ private fun ShortcutKeySeparator() {
}
@Composable
-private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) {
+private fun ShortcutIconKey(key: ShortcutKey.Icon) {
Icon(
painter =
when (key) {
@@ -354,7 +368,7 @@ private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) {
is ShortcutKey.Icon.DrawableIcon -> rememberDrawablePainter(drawable = key.drawable)
},
contentDescription = null,
- modifier = Modifier.align(Alignment.CenterVertically).height(24.dp),
+ modifier = Modifier.height(24.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
@@ -405,7 +419,7 @@ private fun Description(text: String) {
.width(316.dp)
.wrapContentSize(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
- textAlign = TextAlign.Center
+ textAlign = TextAlign.Center,
)
}
@@ -473,3 +487,31 @@ private fun PlusIconContainer() {
modifier = Modifier.padding(vertical = 12.dp).size(24.dp).wrapContentSize(Alignment.Center),
)
}
+
+@Composable
+private fun OutlinedInputField(
+ content: @Composable (() -> Unit)?,
+ placeholder: @Composable () -> Unit,
+ trailingIcon: @Composable () -> Unit,
+ isError: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ OutlinedTextField(
+ value = "",
+ onValueChange = {},
+ placeholder = if (content == null) placeholder else null,
+ prefix = content,
+ singleLine = true,
+ modifier = modifier,
+ trailingIcon = trailingIcon,
+ colors =
+ OutlinedTextFieldDefaults.colors()
+ .copy(
+ focusedIndicatorColor = MaterialTheme.colorScheme.primary,
+ unfocusedIndicatorColor = MaterialTheme.colorScheme.outline,
+ errorIndicatorColor = MaterialTheme.colorScheme.error,
+ ),
+ shape = RoundedCornerShape(50.dp),
+ isError = isError,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 915a66c43a12..f4ba99c6a394 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -28,16 +28,17 @@ import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestRe
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
+import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.res.R
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
class ShortcutCustomizationViewModel
@@ -45,26 +46,12 @@ class ShortcutCustomizationViewModel
constructor(
private val context: Context,
private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor,
-) {
+) : ExclusiveActivatable() {
private var keyDownEventCache: KeyEvent? = null
private val _shortcutCustomizationUiState =
MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive)
- val shortcutCustomizationUiState =
- shortcutCustomizationInteractor.pressedKeys
- .map { keys ->
- // Note that Action Key is excluded as it's already displayed on the UI
- keys.filter {
- it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey()
- }
- }
- .combine(_shortcutCustomizationUiState) { keys, uiState ->
- if (uiState is AddShortcutDialog) {
- uiState.copy(pressedKeys = keys)
- } else {
- uiState
- }
- }
+ val shortcutCustomizationUiState = _shortcutCustomizationUiState.asStateFlow()
fun onShortcutCustomizationRequested(requestInfo: ShortcutCustomizationRequestInfo) {
shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
@@ -92,7 +79,7 @@ constructor(
fun onDialogDismissed() {
_shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
shortcutCustomizationInteractor.onCustomizationRequested(null)
- shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
+ clearSelectedKeyCombination()
}
fun onShortcutKeyCombinationSelected(keyEvent: KeyEvent): Boolean {
@@ -112,7 +99,6 @@ constructor(
suspend fun onSetShortcut() {
val result = shortcutCustomizationInteractor.confirmAndSetShortcutCurrentlyBeingCustomized()
-
_shortcutCustomizationUiState.update { uiState ->
when (result) {
ShortcutCustomizationRequestResult.SUCCESS -> ShortcutCustomizationUiState.Inactive
@@ -158,6 +144,10 @@ constructor(
}
}
+ fun clearSelectedKeyCombination() {
+ shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
+ }
+
private fun getUiStateWithErrorMessage(
uiState: ShortcutCustomizationUiState,
errorMessage: String,
@@ -180,11 +170,41 @@ constructor(
keyDownEventCache = null
}
+ private suspend fun isSelectedKeyCombinationAvailable() =
+ shortcutCustomizationInteractor.isSelectedKeyCombinationAvailable()
+
@AssistedFactory
interface Factory {
fun create(): ShortcutCustomizationViewModel
}
+ override suspend fun onActivated(): Nothing {
+ shortcutCustomizationInteractor.pressedKeys.collect {
+ val keys = filterDefaultCustomShortcutModifierKey(it)
+ val errorMessage = getErrorMessageForPressedKeys(keys)
+
+ _shortcutCustomizationUiState.update { uiState ->
+ if (uiState is AddShortcutDialog) {
+ uiState.copy(pressedKeys = keys, errorMessage = errorMessage)
+ } else {
+ uiState
+ }
+ }
+ }
+ }
+
+ private suspend fun getErrorMessageForPressedKeys(keys: List<ShortcutKey>): String {
+ return if (keys.isEmpty() or isSelectedKeyCombinationAvailable()) {
+ ""
+ }
+ else {
+ context.getString(R.string.shortcut_customizer_key_combination_in_use_error_message)
+ }
+ }
+
+ private fun filterDefaultCustomShortcutModifierKey(keys: List<ShortcutKey>) =
+ keys.filter { it != shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey() }
+
companion object {
private val SUPPORTED_MODIFIERS =
listOf(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 3565b612a3c9..c5d40a0dcf30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -48,7 +48,6 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
@OptIn(FlowPreview::class)
@@ -92,6 +91,7 @@ constructor(
listenForHubToAlternateBouncer()
listenForHubToOccluded()
listenForHubToGone()
+ listenForHubToDreaming()
}
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -177,6 +177,24 @@ constructor(
}
}
+ private fun listenForHubToDreaming() {
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
+ return
+ }
+
+ scope.launch {
+ keyguardInteractor.isAbleToDream
+ .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream }
+ .collect {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "hub to dreaming",
+ keyguardState = KeyguardState.DREAMING,
+ )
+ }
+ }
+ }
+
private fun listenForHubToOccluded() {
if (KeyguardWmStateRefactor.isEnabled) {
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 184f30237e8d..6a354821f628 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -24,7 +24,6 @@ import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAsleepInState
@@ -331,17 +330,9 @@ constructor(
* clock/smartspace/notif icons are visible.
*/
val aodVisibility: Flow<Boolean> =
- combine(
- keyguardInteractor.isDozing,
- keyguardInteractor.isAodAvailable,
- keyguardInteractor.biometricUnlockState,
- ) { isDozing, isAodAvailable, biometricUnlockState ->
- // AOD is visible if we're dozing, unless we are wake and unlocking (where we go
- // directly from AOD to unlocked while dozing).
- isDozing &&
- isAodAvailable &&
- !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
- }
+ transitionInteractor
+ .transitionValue(KeyguardState.AOD)
+ .map { it == 1f }
.distinctUntilChanged()
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
index eb005f226cf1..454ba9af5745 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
@@ -76,9 +76,8 @@ constructor(
private var manualUnlockAmount: Float? = null
/**
- * Called from [OverviewProxyService] to provide us with the launcher unlock animation
- * controller, which can be used to start and update the unlock animation in the launcher
- * process.
+ * Called from Launcher to provide us with the launcher unlock animation controller, which can
+ * be used to start and update the unlock animation in the launcher process.
*/
override fun setLauncherUnlockController(
activityClass: String,
@@ -117,7 +116,7 @@ constructor(
launcher.prepareForUnlock(
false,
Rect(),
- 0
+ 0,
) // TODO(b/293894758): Add smartspace animation support.
}
}
@@ -134,14 +133,14 @@ constructor(
Log.e(
TAG,
"Called prepareForUnlock(), but not playUnlockAnimation(). " +
- "Failing-safe by calling setUnlockAmount(1f)"
+ "Failing-safe by calling setUnlockAmount(1f)",
)
setUnlockAmount(1f, forceIfAnimating = true)
} else if (manualUnlockSetButNotFullyVisible) {
Log.e(
TAG,
"Unlock has ended, but manual unlock amount != 1f. " +
- "Failing-safe by calling setUnlockAmount(1f)"
+ "Failing-safe by calling setUnlockAmount(1f)",
)
setUnlockAmount(1f, forceIfAnimating = true)
}
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 b531c7fa49ec..a6d15b96547d 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
@@ -100,7 +100,11 @@ constructor(
override val windowBlurRadius: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsExpanded =
- transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+ if (Flags.notificationShadeBlur()) {
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ } else {
+ emptyFlow()
+ },
flowWhenShadeIsNotExpanded =
transitionAnimation.sharedFlow(
duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
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 89dcbf6aa52b..e25e4e5af425 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
@@ -107,7 +107,11 @@ constructor(
override val windowBlurRadius: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsExpanded =
- transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+ if (Flags.notificationShadeBlur()) {
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ } else {
+ emptyFlow()
+ },
flowWhenShadeIsNotExpanded =
transitionAnimation.sharedFlow(
duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
index 4d3e27265cea..3c126aa23fef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -26,12 +27,16 @@ import com.android.systemui.keyguard.ui.transitions.BlurConfig
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
@SysUISingleton
class OccludedToPrimaryBouncerTransitionViewModel
@Inject
-constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
- PrimaryBouncerTransition {
+constructor(
+ shadeDependentFlows: ShadeDependentFlows,
+ blurConfig: BlurConfig,
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
@@ -41,8 +46,21 @@ constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFl
.setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, PRIMARY_BOUNCER))
override val windowBlurRadius: Flow<Float> =
- transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ if (Flags.notificationShadeBlur()) {
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ } else {
+ emptyFlow()
+ },
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+ )
override val notificationBlurRadius: Flow<Float> =
- transitionAnimation.immediatelyTransitionTo(0.0f)
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+ flowWhenShadeIsNotExpanded = emptyFlow(),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index c53a408a88e1..9b01803f1fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -32,6 +33,7 @@ import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
/**
* Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -78,7 +80,11 @@ constructor(
override val windowBlurRadius: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsExpanded =
- transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx),
+ if (Flags.notificationShadeBlur()) {
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ } else {
+ emptyFlow()
+ },
flowWhenShadeIsNotExpanded =
transitionAnimation.sharedFlow(
duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
index fe1708efea2f..0f0e7b6faa66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -26,12 +27,16 @@ import com.android.systemui.keyguard.ui.transitions.BlurConfig
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
@SysUISingleton
class PrimaryBouncerToOccludedTransitionViewModel
@Inject
-constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFlow) :
- PrimaryBouncerTransition {
+constructor(
+ shadeDependentFlows: ShadeDependentFlows,
+ blurConfig: BlurConfig,
+ animationFlow: KeyguardTransitionAnimationFlow,
+) : PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
@@ -41,7 +46,16 @@ constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitio
.setupWithoutSceneContainer(edge = Edge.create(PRIMARY_BOUNCER, OCCLUDED))
override val windowBlurRadius: Flow<Float> =
- transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded =
+ if (Flags.notificationShadeBlur()) {
+ transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ } else {
+ emptyFlow()
+ },
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx),
+ )
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0.0f)
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
new file mode 100644
index 000000000000..ece97bd27df7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.lowlightclock
+
+import android.annotation.IntDef
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.util.Log
+import com.android.systemui.Dumpable
+import com.android.systemui.lowlightclock.dagger.LowLightModule.LIGHT_SENSOR
+import com.android.systemui.util.sensors.AsyncSensorManager
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Monitors ambient light signals, applies a debouncing algorithm, and produces the current ambient
+ * light mode.
+ *
+ * @property algorithm the debounce algorithm which transforms light sensor events into an ambient
+ * light mode.
+ * @property sensorManager the sensor manager used to register sensor event updates.
+ */
+class AmbientLightModeMonitor
+@Inject
+constructor(
+ private val algorithm: Optional<DebounceAlgorithm>,
+ private val sensorManager: AsyncSensorManager,
+ @Named(LIGHT_SENSOR) private val lightSensor: Optional<Sensor>,
+) : Dumpable {
+ companion object {
+ private const val TAG = "AmbientLightModeMonitor"
+ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+ const val AMBIENT_LIGHT_MODE_LIGHT = 0
+ const val AMBIENT_LIGHT_MODE_DARK = 1
+ const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
+ }
+
+ // Represents all ambient light modes.
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
+ annotation class AmbientLightMode
+
+ /**
+ * Start monitoring the current ambient light mode.
+ *
+ * @param callback callback that gets triggered when the ambient light mode changes.
+ */
+ fun start(callback: Callback) {
+ if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
+
+ if (lightSensor.isEmpty) {
+ if (DEBUG) Log.w(TAG, "light sensor not available")
+ return
+ }
+
+ if (algorithm.isEmpty) {
+ if (DEBUG) Log.w(TAG, "debounce algorithm not available")
+ return
+ }
+
+ algorithm.get().start(callback)
+ sensorManager.registerListener(
+ mSensorEventListener,
+ lightSensor.get(),
+ SensorManager.SENSOR_DELAY_NORMAL,
+ )
+ }
+
+ /** Stop monitoring the current ambient light mode. */
+ fun stop() {
+ if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
+
+ if (algorithm.isPresent) {
+ algorithm.get().stop()
+ }
+ sensorManager.unregisterListener(mSensorEventListener)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println()
+ pw.println("Ambient light mode monitor:")
+ pw.println(" lightSensor=$lightSensor")
+ pw.println()
+ }
+
+ private val mSensorEventListener: SensorEventListener =
+ object : SensorEventListener {
+ override fun onSensorChanged(event: SensorEvent) {
+ if (event.values.isEmpty()) {
+ if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
+ return
+ }
+
+ if (algorithm.isPresent) {
+ algorithm.get().onUpdateLightSensorEvent(event.values[0])
+ }
+ }
+
+ override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
+ // Do nothing.
+ }
+ }
+
+ /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
+ interface Callback {
+ fun onChange(@AmbientLightMode mode: Int)
+ }
+
+ /** Interface of the algorithm that transforms light sensor events to an ambient light mode. */
+ interface DebounceAlgorithm {
+ // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
+ fun start(callback: Callback?)
+
+ fun stop()
+
+ fun onUpdateLightSensorEvent(value: Float)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java
new file mode 100644
index 000000000000..8cc399b0a22b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ChargingStatusProvider.java
@@ -0,0 +1,258 @@
+/*
+ * 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.lowlightclock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.BatteryManager;
+import android.os.RemoteException;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.Preconditions;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
+
+import java.text.NumberFormat;
+
+import javax.inject.Inject;
+
+/**
+ * Provides charging status as a string to a registered callback such that it can be displayed to
+ * the user (e.g. on the low-light clock).
+ * TODO(b/223681352): Make this code shareable with {@link KeyguardIndicationController}.
+ */
+public class ChargingStatusProvider {
+ private static final String TAG = "ChargingStatusProvider";
+
+ private final Resources mResources;
+ private final Context mContext;
+ private final IBatteryStats mBatteryInfo;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final BatteryState mBatteryState = new BatteryState();
+ // This callback is registered with KeyguardUpdateMonitor, which only keeps weak references to
+ // its callbacks. Therefore, an explicit reference needs to be kept here to avoid the
+ // callback being GC'd.
+ private ChargingStatusCallback mChargingStatusCallback;
+
+ private Callback mCallback;
+
+ @Inject
+ public ChargingStatusProvider(
+ Context context,
+ @Main Resources resources,
+ IBatteryStats iBatteryStats,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mContext = context;
+ mResources = resources;
+ mBatteryInfo = iBatteryStats;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ /**
+ * Start using the {@link ChargingStatusProvider}.
+ * @param callback A callback to be called when the charging status changes.
+ */
+ public void startUsing(Callback callback) {
+ Preconditions.checkState(
+ mCallback == null, "ChargingStatusProvider already started!");
+ mCallback = callback;
+ mChargingStatusCallback = new ChargingStatusCallback();
+ mKeyguardUpdateMonitor.registerCallback(mChargingStatusCallback);
+ reportStatusToCallback();
+ }
+
+ /**
+ * Stop using the {@link ChargingStatusProvider}.
+ */
+ public void stopUsing() {
+ mCallback = null;
+
+ if (mChargingStatusCallback != null) {
+ mKeyguardUpdateMonitor.removeCallback(mChargingStatusCallback);
+ mChargingStatusCallback = null;
+ }
+ }
+
+ private String computeChargingString() {
+ if (!mBatteryState.isValid()) {
+ return null;
+ }
+
+ int chargingId;
+
+ if (mBatteryState.isBatteryDefender()) {
+ return mResources.getString(
+ R.string.keyguard_plugged_in_charging_limited,
+ mBatteryState.getBatteryLevelAsPercentage());
+ } else if (mBatteryState.isPowerCharged()) {
+ return mResources.getString(R.string.keyguard_charged);
+ }
+
+ final long chargingTimeRemaining = mBatteryState.getChargingTimeRemaining(mBatteryInfo);
+ final boolean hasChargingTime = chargingTimeRemaining > 0;
+ if (mBatteryState.isPowerPluggedInWired()) {
+ switch (mBatteryState.getChargingSpeed(mContext)) {
+ case BatteryStatus.CHARGING_FAST:
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_fast
+ : R.string.keyguard_plugged_in_charging_fast;
+ break;
+ case BatteryStatus.CHARGING_SLOWLY:
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_slowly
+ : R.string.keyguard_plugged_in_charging_slowly;
+ break;
+ default:
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time
+ : R.string.keyguard_plugged_in;
+ break;
+ }
+ } else if (mBatteryState.isPowerPluggedInWireless()) {
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_wireless
+ : R.string.keyguard_plugged_in_wireless;
+ } else if (mBatteryState.isPowerPluggedInDocked()) {
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_dock
+ : R.string.keyguard_plugged_in_dock;
+ } else {
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time
+ : R.string.keyguard_plugged_in;
+ }
+
+ final String percentage = mBatteryState.getBatteryLevelAsPercentage();
+ if (hasChargingTime) {
+ final String chargingTimeFormatted =
+ Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ mContext, chargingTimeRemaining);
+ return mResources.getString(chargingId, chargingTimeFormatted,
+ percentage);
+ } else {
+ return mResources.getString(chargingId, percentage);
+ }
+ }
+
+ private void reportStatusToCallback() {
+ if (mCallback != null) {
+ final boolean shouldShowStatus =
+ mBatteryState.isPowerPluggedIn() || mBatteryState.isBatteryDefenderEnabled();
+ mCallback.onChargingStatusChanged(shouldShowStatus, computeChargingString());
+ }
+ }
+
+ private class ChargingStatusCallback extends KeyguardUpdateMonitorCallback {
+ @Override
+ public void onRefreshBatteryInfo(BatteryStatus status) {
+ mBatteryState.setBatteryStatus(status);
+ reportStatusToCallback();
+ }
+ }
+
+ /***
+ * A callback to be called when the charging status changes.
+ */
+ public interface Callback {
+ /***
+ * Called when the charging status changes.
+ * @param shouldShowStatus Whether or not to show a charging status message.
+ * @param statusMessage A charging status message.
+ */
+ void onChargingStatusChanged(boolean shouldShowStatus, String statusMessage);
+ }
+
+ /***
+ * A wrapper around {@link BatteryStatus} for fetching various properties of the current
+ * battery and charging state.
+ */
+ private static class BatteryState {
+ private BatteryStatus mBatteryStatus;
+
+ public void setBatteryStatus(BatteryStatus batteryStatus) {
+ mBatteryStatus = batteryStatus;
+ }
+
+ public boolean isValid() {
+ return mBatteryStatus != null;
+ }
+
+ public long getChargingTimeRemaining(IBatteryStats batteryInfo) {
+ try {
+ return isPowerPluggedIn() ? batteryInfo.computeChargeTimeRemaining() : -1;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IBatteryStats: ", e);
+ return -1;
+ }
+ }
+
+ public boolean isBatteryDefenderEnabled() {
+ return isValid() && mBatteryStatus.isPluggedIn() && isBatteryDefender();
+ }
+
+ public boolean isBatteryDefender() {
+ return isValid() && mBatteryStatus.isBatteryDefender();
+ }
+
+ public int getBatteryLevel() {
+ return isValid() ? mBatteryStatus.level : 0;
+ }
+
+ public int getChargingSpeed(Context context) {
+ return isValid() ? mBatteryStatus.getChargingSpeed(context) : 0;
+ }
+
+ public boolean isPowerCharged() {
+ return isValid() && mBatteryStatus.isCharged();
+ }
+
+ public boolean isPowerPluggedIn() {
+ return isValid() && mBatteryStatus.isPluggedIn() && isChargingOrFull();
+ }
+
+ public boolean isPowerPluggedInWired() {
+ return isValid()
+ && mBatteryStatus.isPluggedInWired()
+ && isChargingOrFull();
+ }
+
+ public boolean isPowerPluggedInWireless() {
+ return isValid()
+ && mBatteryStatus.isPluggedInWireless()
+ && isChargingOrFull();
+ }
+
+ public boolean isPowerPluggedInDocked() {
+ return isValid() && mBatteryStatus.isPluggedInDock() && isChargingOrFull();
+ }
+
+ private boolean isChargingOrFull() {
+ return isValid()
+ && (mBatteryStatus.status == BatteryManager.BATTERY_STATUS_CHARGING
+ || mBatteryStatus.isCharged());
+ }
+
+ private String getBatteryLevelAsPercentage() {
+ return NumberFormat.getPercentInstance().format(getBatteryLevel() / 100f);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
new file mode 100644
index 000000000000..4c1da0198498
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.lowlightclock
+
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+class DirectBootCondition
+@Inject
+constructor(
+ broadcastDispatcher: BroadcastDispatcher,
+ private val userManager: UserManager,
+ @Application private val coroutineScope: CoroutineScope,
+) : Condition(coroutineScope) {
+ private var job: Job? = null
+ private val directBootFlow =
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(Intent.ACTION_USER_UNLOCKED))
+ .map { !userManager.isUserUnlocked }
+ .cancellable()
+ .distinctUntilChanged()
+
+ override fun start() {
+ job = coroutineScope.launch { directBootFlow.collect { updateCondition(it) } }
+ updateCondition(!userManager.isUserUnlocked)
+ }
+
+ override fun stop() {
+ job?.cancel()
+ }
+
+ override fun getStartStrategy(): Int {
+ return START_EAGERLY
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
new file mode 100644
index 000000000000..7f21d0707f63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
@@ -0,0 +1,137 @@
+/*
+ * 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.lowlightclock;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.commandline.Command;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * This condition registers for and fulfills cmd shell commands to force a device into or out of
+ * low-light conditions.
+ */
+public class ForceLowLightCondition extends Condition {
+ /**
+ * Command root
+ */
+ public static final String COMMAND_ROOT = "low-light";
+ /**
+ * Command for forcing device into low light.
+ */
+ public static final String COMMAND_ENABLE_LOW_LIGHT = "enable";
+
+ /**
+ * Command for preventing a device from entering low light.
+ */
+ public static final String COMMAND_DISABLE_LOW_LIGHT = "disable";
+
+ /**
+ * Command for clearing previously forced low-light conditions.
+ */
+ public static final String COMMAND_CLEAR_LOW_LIGHT = "clear";
+
+ private static final String TAG = "ForceLowLightCondition";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Default Constructor.
+ *
+ * @param commandRegistry command registry to register commands with.
+ */
+ @Inject
+ public ForceLowLightCondition(
+ @Application CoroutineScope scope,
+ CommandRegistry commandRegistry
+ ) {
+ super(scope, null, true);
+
+ if (DEBUG) {
+ Log.d(TAG, "registering commands");
+ }
+ commandRegistry.registerCommand(COMMAND_ROOT, () -> new Command() {
+ @Override
+ public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
+ if (args.size() != 1) {
+ pw.println("no command specified");
+ help(pw);
+ return;
+ }
+
+ final String cmd = args.get(0);
+
+ if (TextUtils.equals(cmd, COMMAND_ENABLE_LOW_LIGHT)) {
+ logAndPrint(pw, "forcing low light");
+ updateCondition(true);
+ } else if (TextUtils.equals(cmd, COMMAND_DISABLE_LOW_LIGHT)) {
+ logAndPrint(pw, "forcing to not enter low light");
+ updateCondition(false);
+ } else if (TextUtils.equals(cmd, COMMAND_CLEAR_LOW_LIGHT)) {
+ logAndPrint(pw, "clearing any forced low light");
+ clearCondition();
+ } else {
+ pw.println("invalid command");
+ help(pw);
+ }
+ }
+
+ @Override
+ public void help(@NonNull PrintWriter pw) {
+ pw.println("Usage: adb shell cmd statusbar low-light <cmd>");
+ pw.println("Supported commands:");
+ pw.println(" - enable");
+ pw.println(" forces device into low-light");
+ pw.println(" - disable");
+ pw.println(" forces device to not enter low-light");
+ pw.println(" - clear");
+ pw.println(" clears any previously forced state");
+ }
+
+ private void logAndPrint(PrintWriter pw, String message) {
+ pw.println(message);
+ if (DEBUG) {
+ Log.d(TAG, message);
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void start() {
+ }
+
+ @Override
+ protected void stop() {
+ }
+
+ @Override
+ protected int getStartStrategy() {
+ return START_EAGERLY;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java
new file mode 100644
index 000000000000..6de599803a57
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockAnimationProvider.java
@@ -0,0 +1,127 @@
+/*
+ * 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.lowlightclock;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.app.animation.Interpolators;
+import com.android.dream.lowlight.util.TruncatedInterpolator;
+import com.android.systemui.lowlightclock.dagger.LowLightModule;
+import com.android.systemui.statusbar.CrossFadeHelper;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/***
+ * A class that provides the animations used by the low-light clock.
+ *
+ * The entry and exit animations are opposites, with the only difference being a delay before the
+ * text fades in on entry.
+ */
+public class LowLightClockAnimationProvider {
+ private final int mYTranslationAnimationInStartOffset;
+ private final long mYTranslationAnimationInDurationMillis;
+ private final long mAlphaAnimationInStartDelayMillis;
+ private final long mAlphaAnimationDurationMillis;
+
+ /**
+ * Custom interpolator used for the translate out animation, which uses an emphasized easing
+ * like the translate in animation, but is scaled to match the length of the alpha animation.
+ */
+ private final Interpolator mTranslationOutInterpolator;
+
+ @Inject
+ public LowLightClockAnimationProvider(
+ @Named(LowLightModule.Y_TRANSLATION_ANIMATION_OFFSET)
+ int yTranslationAnimationInStartOffset,
+ @Named(LowLightModule.Y_TRANSLATION_ANIMATION_DURATION_MILLIS)
+ long yTranslationAnimationInDurationMillis,
+ @Named(LowLightModule.ALPHA_ANIMATION_IN_START_DELAY_MILLIS)
+ long alphaAnimationInStartDelayMillis,
+ @Named(LowLightModule.ALPHA_ANIMATION_DURATION_MILLIS)
+ long alphaAnimationDurationMillis) {
+ mYTranslationAnimationInStartOffset = yTranslationAnimationInStartOffset;
+ mYTranslationAnimationInDurationMillis = yTranslationAnimationInDurationMillis;
+ mAlphaAnimationInStartDelayMillis = alphaAnimationInStartDelayMillis;
+ mAlphaAnimationDurationMillis = alphaAnimationDurationMillis;
+
+ mTranslationOutInterpolator = new TruncatedInterpolator(Interpolators.EMPHASIZED,
+ /*originalDuration=*/ mYTranslationAnimationInDurationMillis,
+ /*newDuration=*/ mAlphaAnimationDurationMillis);
+ }
+
+ /***
+ * Provides an animation for when the given views become visible.
+ * @param views Any number of views to animate in together.
+ */
+ public Animator provideAnimationIn(View... views) {
+ final AnimatorSet animatorSet = new AnimatorSet();
+
+ for (View view : views) {
+ if (view == null) continue;
+ // Set the alpha to 0 to start because the alpha animation has a start delay.
+ CrossFadeHelper.fadeOut(view, 0f, false);
+
+ final Animator alphaAnimator =
+ ObjectAnimator.ofFloat(view, View.ALPHA, 1f);
+ alphaAnimator.setStartDelay(mAlphaAnimationInStartDelayMillis);
+ alphaAnimator.setDuration(mAlphaAnimationDurationMillis);
+ alphaAnimator.setInterpolator(Interpolators.LINEAR);
+
+ final Animator positionAnimator = ObjectAnimator
+ .ofFloat(view, View.TRANSLATION_Y, mYTranslationAnimationInStartOffset, 0f);
+ positionAnimator.setDuration(mYTranslationAnimationInDurationMillis);
+ positionAnimator.setInterpolator(Interpolators.EMPHASIZED);
+
+ // The position animator must be started first since the alpha animator has a start
+ // delay.
+ animatorSet.playTogether(positionAnimator, alphaAnimator);
+ }
+
+ return animatorSet;
+ }
+
+ /***
+ * Provides an animation for when the given views are going out of view.
+ * @param views Any number of views to animate out.
+ */
+ public Animator provideAnimationOut(View... views) {
+ final AnimatorSet animatorSet = new AnimatorSet();
+
+ for (View view : views) {
+ if (view == null) continue;
+ final Animator alphaAnimator =
+ ObjectAnimator.ofFloat(view, View.ALPHA, 0f);
+ alphaAnimator.setDuration(mAlphaAnimationDurationMillis);
+ alphaAnimator.setInterpolator(Interpolators.LINEAR);
+
+ final Animator positionAnimator = ObjectAnimator
+ .ofFloat(view, View.TRANSLATION_Y, mYTranslationAnimationInStartOffset);
+ // Use the same duration as the alpha animation plus our custom interpolator.
+ positionAnimator.setDuration(mAlphaAnimationDurationMillis);
+ positionAnimator.setInterpolator(mTranslationOutInterpolator);
+ animatorSet.playTogether(alphaAnimator, positionAnimator);
+ }
+
+ return animatorSet;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
new file mode 100644
index 000000000000..e91be5028777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
@@ -0,0 +1,77 @@
+/*
+ * 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.lowlightclock;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.shared.condition.Condition;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition for monitoring when the device enters and exits lowlight mode.
+ */
+public class LowLightCondition extends Condition {
+ private final AmbientLightModeMonitor mAmbientLightModeMonitor;
+ private final UiEventLogger mUiEventLogger;
+
+ @Inject
+ public LowLightCondition(@Application CoroutineScope scope,
+ AmbientLightModeMonitor ambientLightModeMonitor,
+ UiEventLogger uiEventLogger) {
+ super(scope);
+ mAmbientLightModeMonitor = ambientLightModeMonitor;
+ mUiEventLogger = uiEventLogger;
+ }
+
+ @Override
+ protected void start() {
+ mAmbientLightModeMonitor.start(this::onLowLightChanged);
+ }
+
+ @Override
+ protected void stop() {
+ mAmbientLightModeMonitor.stop();
+
+ // Reset condition met to false.
+ updateCondition(false);
+ }
+
+ @Override
+ protected int getStartStrategy() {
+ // As this condition keeps the lowlight sensor active, it should only run when needed.
+ return START_WHEN_NEEDED;
+ }
+
+ private void onLowLightChanged(int lowLightMode) {
+ if (lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) {
+ // Ignore undecided mode changes.
+ return;
+ }
+
+ final boolean isLowLight = lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK;
+ if (isLowLight == isConditionMet()) {
+ // No change in condition, don't do anything.
+ return;
+ }
+ mUiEventLogger.log(isLowLight ? LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
+ : LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
+ updateCondition(isLowLight);
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt
index 9a7f99f3247b..9a9d813b18c5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromNotificationsShadeToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDisplayController.kt
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.scene.ui.composable.transitions
+package com.android.systemui.lowlightclock
-import androidx.compose.animation.core.tween
-import com.android.compose.animation.scene.TransitionBuilder
-import kotlin.time.Duration.Companion.milliseconds
+interface LowLightDisplayController {
+ fun isDisplayBrightnessModeSupported(): Boolean
-fun TransitionBuilder.notificationsShadeToQuickSettingsShadeTransition(
- durationScale: Double = 1.0
-) {
- spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+ fun setDisplayBrightnessModeEnabled(enabled: Boolean)
}
-
-private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt
new file mode 100644
index 000000000000..b99aeb6eeacc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightDockEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.lowlightclock
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class LowLightDockEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Ambient light changed from light to dark") AMBIENT_LIGHT_TO_DARK(999),
+ @UiEvent(doc = "The low light mode has started") LOW_LIGHT_STARTED(1000),
+ @UiEvent(doc = "Ambient light changed from dark to light") AMBIENT_LIGHT_TO_LIGHT(1001),
+ @UiEvent(doc = "The low light mode has stopped") LOW_LIGHT_STOPPED(1002);
+
+ override fun getId(): Int {
+ return id
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt
new file mode 100644
index 000000000000..11d75215edf5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightLogger.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.lowlightclock
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.lowlightclock.dagger.LowLightLog
+import javax.inject.Inject
+
+/** Logs to a {@link LogBuffer} anything related to low-light features. */
+class LowLightLogger @Inject constructor(@LowLightLog private val buffer: LogBuffer) {
+ /** Logs a debug message to the buffer. */
+ fun d(tag: String, message: String) = buffer.log(tag, LogLevel.DEBUG, message)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java
new file mode 100644
index 000000000000..912ace7675d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightMonitor.java
@@ -0,0 +1,133 @@
+/*
+ * 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.lowlightclock;
+
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR;
+import static com.android.systemui.dreams.dagger.DreamModule.LOW_LIGHT_DREAM_SERVICE;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
+import static com.android.systemui.lowlightclock.dagger.LowLightModule.LOW_LIGHT_PRECONDITIONS;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+
+import androidx.annotation.Nullable;
+
+import com.android.dream.lowlight.LowLightDreamManager;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import dagger.Lazy;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Tracks environment (low-light or not) in order to correctly show or hide a low-light clock while
+ * dreaming.
+ */
+public class LowLightMonitor extends ConditionalCoreStartable implements Monitor.Callback,
+ ScreenLifecycle.Observer {
+ private static final String TAG = "LowLightMonitor";
+
+ private final Lazy<LowLightDreamManager> mLowLightDreamManager;
+ private final Monitor mConditionsMonitor;
+ private final Lazy<Set<Condition>> mLowLightConditions;
+ private Monitor.Subscription.Token mSubscriptionToken;
+ private ScreenLifecycle mScreenLifecycle;
+ private final LowLightLogger mLogger;
+
+ private final ComponentName mLowLightDreamService;
+
+ private final PackageManager mPackageManager;
+
+ @Inject
+ public LowLightMonitor(Lazy<LowLightDreamManager> lowLightDreamManager,
+ @SystemUser Monitor conditionsMonitor,
+ @Named(LOW_LIGHT_PRECONDITIONS) Lazy<Set<Condition>> lowLightConditions,
+ ScreenLifecycle screenLifecycle,
+ LowLightLogger lowLightLogger,
+ @Nullable @Named(LOW_LIGHT_DREAM_SERVICE) ComponentName lowLightDreamService,
+ PackageManager packageManager) {
+ super(conditionsMonitor);
+ mLowLightDreamManager = lowLightDreamManager;
+ mConditionsMonitor = conditionsMonitor;
+ mLowLightConditions = lowLightConditions;
+ mScreenLifecycle = screenLifecycle;
+ mLogger = lowLightLogger;
+ mLowLightDreamService = lowLightDreamService;
+ mPackageManager = packageManager;
+ }
+
+ @Override
+ public void onConditionsChanged(boolean allConditionsMet) {
+ mLogger.d(TAG, "Low light enabled: " + allConditionsMet);
+
+ mLowLightDreamManager.get().setAmbientLightMode(allConditionsMet
+ ? AMBIENT_LIGHT_MODE_LOW_LIGHT : AMBIENT_LIGHT_MODE_REGULAR);
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ if (mSubscriptionToken == null) {
+ mLogger.d(TAG, "Screen turned on. Subscribing to low light conditions.");
+
+ mSubscriptionToken = mConditionsMonitor.addSubscription(
+ new Monitor.Subscription.Builder(this)
+ .addConditions(mLowLightConditions.get())
+ .build());
+ }
+ }
+
+
+ @Override
+ public void onScreenTurnedOff() {
+ if (mSubscriptionToken != null) {
+ mLogger.d(TAG, "Screen turned off. Removing subscription to low light conditions.");
+
+ mConditionsMonitor.removeSubscription(mSubscriptionToken);
+ mSubscriptionToken = null;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ if (mLowLightDreamService != null) {
+ // Note that the dream service is disabled by default. This prevents the dream from
+ // appearing in settings on devices that don't have it explicitly excluded (done in
+ // the settings overlay). Therefore, the component is enabled if it is to be used
+ // here.
+ mPackageManager.setComponentEnabledSetting(
+ mLowLightDreamService,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP
+ );
+ } else {
+ // If there is no low light dream service, do not observe conditions.
+ return;
+ }
+
+ mScreenLifecycle.addObserver(this);
+ if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
+ onScreenTurnedOn();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
new file mode 100644
index 000000000000..fd6ce1762a28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
@@ -0,0 +1,87 @@
+/*
+ * 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.lowlightclock;
+
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.util.settings.SecureSettings;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import javax.inject.Inject;
+
+/**
+ * Condition for monitoring if the screensaver setting is enabled.
+ */
+public class ScreenSaverEnabledCondition extends Condition {
+ private static final String TAG = ScreenSaverEnabledCondition.class.getSimpleName();
+
+ private final boolean mScreenSaverEnabledByDefaultConfig;
+ private final SecureSettings mSecureSettings;
+
+ private final ContentObserver mScreenSaverSettingObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateScreenSaverEnabledSetting();
+ }
+ };
+
+ @Inject
+ public ScreenSaverEnabledCondition(@Application CoroutineScope scope, @Main Resources resources,
+ SecureSettings secureSettings) {
+ super(scope);
+ mScreenSaverEnabledByDefaultConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsEnabledByDefault);
+ mSecureSettings = secureSettings;
+ }
+
+ @Override
+ protected void start() {
+ mSecureSettings.registerContentObserverForUserSync(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ mScreenSaverSettingObserver, UserHandle.USER_CURRENT);
+ updateScreenSaverEnabledSetting();
+ }
+
+ @Override
+ protected void stop() {
+ mSecureSettings.unregisterContentObserverSync(mScreenSaverSettingObserver);
+ }
+
+ @Override
+ protected int getStartStrategy() {
+ return START_EAGERLY;
+ }
+
+ private void updateScreenSaverEnabledSetting() {
+ final boolean enabled = mSecureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ mScreenSaverEnabledByDefaultConfig ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0;
+ if (!enabled) {
+ Log.i(TAG, "Disabling low-light clock because screen saver has been disabled");
+ }
+ updateCondition(enabled);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt
new file mode 100644
index 000000000000..0819664c921c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.lowlightclock.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for logging related to low light features. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class LowLightLog
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java
new file mode 100644
index 000000000000..c08be51c0699
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/dagger/LowLightModule.java
@@ -0,0 +1,150 @@
+/*
+ * 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.lowlightclock.dagger;
+
+import android.content.res.Resources;
+import android.hardware.Sensor;
+
+import com.android.dream.lowlight.dagger.LowLightDreamModule;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.communal.DeviceInactiveCondition;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogBufferFactory;
+import com.android.systemui.lowlightclock.AmbientLightModeMonitor;
+import com.android.systemui.lowlightclock.DirectBootCondition;
+import com.android.systemui.lowlightclock.ForceLowLightCondition;
+import com.android.systemui.lowlightclock.LowLightCondition;
+import com.android.systemui.lowlightclock.LowLightDisplayController;
+import com.android.systemui.lowlightclock.LowLightMonitor;
+import com.android.systemui.lowlightclock.ScreenSaverEnabledCondition;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.condition.Condition;
+
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+
+import javax.inject.Named;
+
+@Module(includes = LowLightDreamModule.class)
+public abstract class LowLightModule {
+ public static final String Y_TRANSLATION_ANIMATION_OFFSET =
+ "y_translation_animation_offset";
+ public static final String Y_TRANSLATION_ANIMATION_DURATION_MILLIS =
+ "y_translation_animation_duration_millis";
+ public static final String ALPHA_ANIMATION_IN_START_DELAY_MILLIS =
+ "alpha_animation_in_start_delay_millis";
+ public static final String ALPHA_ANIMATION_DURATION_MILLIS =
+ "alpha_animation_duration_millis";
+ public static final String LOW_LIGHT_PRECONDITIONS = "low_light_preconditions";
+ public static final String LIGHT_SENSOR = "low_light_monitor_light_sensor";
+
+
+ /**
+ * Provides a {@link LogBuffer} for logs related to low-light features.
+ */
+ @Provides
+ @SysUISingleton
+ @LowLightLog
+ public static LogBuffer provideLowLightLogBuffer(LogBufferFactory factory) {
+ return factory.create("LowLightLog", 250);
+ }
+
+ @Binds
+ @IntoSet
+ @Named(LOW_LIGHT_PRECONDITIONS)
+ abstract Condition bindScreenSaverEnabledCondition(ScreenSaverEnabledCondition condition);
+
+ @Provides
+ @IntoSet
+ @Named(com.android.systemui.lowlightclock.dagger.LowLightModule.LOW_LIGHT_PRECONDITIONS)
+ static Condition provideLowLightCondition(LowLightCondition lowLightCondition,
+ DirectBootCondition directBootCondition) {
+ // Start lowlight if we are either in lowlight or in direct boot. The ordering of the
+ // conditions matters here since we don't want to start the lowlight condition if
+ // we are in direct boot mode.
+ return directBootCondition.or(lowLightCondition);
+ }
+
+ @Binds
+ @IntoSet
+ @Named(LOW_LIGHT_PRECONDITIONS)
+ abstract Condition bindForceLowLightCondition(ForceLowLightCondition condition);
+
+ @Binds
+ @IntoSet
+ @Named(LOW_LIGHT_PRECONDITIONS)
+ abstract Condition bindDeviceInactiveCondition(DeviceInactiveCondition condition);
+
+ @BindsOptionalOf
+ abstract LowLightDisplayController bindsLowLightDisplayController();
+
+ @BindsOptionalOf
+ @Named(LIGHT_SENSOR)
+ abstract Sensor bindsLightSensor();
+
+ @BindsOptionalOf
+ abstract AmbientLightModeMonitor.DebounceAlgorithm bindsDebounceAlgorithm();
+
+ /**
+ *
+ */
+ @Provides
+ @Named(Y_TRANSLATION_ANIMATION_OFFSET)
+ static int providesAnimationInOffset(@Main Resources resources) {
+ return resources.getDimensionPixelOffset(
+ R.dimen.low_light_clock_translate_animation_offset);
+ }
+
+ /**
+ *
+ */
+ @Provides
+ @Named(Y_TRANSLATION_ANIMATION_DURATION_MILLIS)
+ static long providesAnimationDurationMillis(@Main Resources resources) {
+ return resources.getInteger(R.integer.low_light_clock_translate_animation_duration_ms);
+ }
+
+ /**
+ *
+ */
+ @Provides
+ @Named(ALPHA_ANIMATION_IN_START_DELAY_MILLIS)
+ static long providesAlphaAnimationInStartDelayMillis(@Main Resources resources) {
+ return resources.getInteger(R.integer.low_light_clock_alpha_animation_in_start_delay_ms);
+ }
+
+ /**
+ *
+ */
+ @Provides
+ @Named(ALPHA_ANIMATION_DURATION_MILLIS)
+ static long providesAlphaAnimationDurationMillis(@Main Resources resources) {
+ return resources.getInteger(R.integer.low_light_clock_alpha_animation_duration_ms);
+ }
+ /** Inject into LowLightMonitor. */
+ @Binds
+ @IntoMap
+ @ClassKey(LowLightMonitor.class)
+ abstract CoreStartable bindLowLightMonitor(LowLightMonitor lowLightMonitor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index f1f299aac2b4..52749c54b9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -1268,13 +1268,21 @@ class LegacyMediaDataManagerImpl(
}
private fun getResumeMediaAction(action: Runnable): MediaAction {
+ val iconId =
+ if (Flags.mediaControlsUiUpdate()) {
+ R.drawable.ic_media_play_button
+ } else {
+ R.drawable.ic_media_play
+ }
return MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play)
- .setTint(themeText)
- .loadDrawable(context),
+ Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
context.getString(R.string.controls_media_resume),
- context.getDrawable(R.drawable.ic_media_play_container),
+ if (Flags.mediaControlsUiUpdate()) {
+ context.getDrawable(R.drawable.ic_media_play_button_container)
+ } else {
+ context.getDrawable(R.drawable.ic_media_play_container)
+ },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index a176e0c1c2a6..8bb7303a8386 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -43,7 +43,9 @@ import android.support.v4.media.MediaMetadataCompat
import android.text.TextUtils
import android.util.Log
import androidx.media.utils.MediaConstants
+import com.android.app.tracing.coroutines.asyncTraced as async
import com.android.app.tracing.coroutines.traceCoroutine
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -67,7 +69,6 @@ import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.asyncTraced as async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
@@ -511,13 +512,21 @@ constructor(
sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
private fun getResumeMediaAction(action: Runnable): MediaAction {
+ val iconId =
+ if (Flags.mediaControlsUiUpdate()) {
+ R.drawable.ic_media_play_button
+ } else {
+ R.drawable.ic_media_play
+ }
return MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play)
- .setTint(themeText)
- .loadDrawable(context),
+ Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
context.getString(R.string.controls_media_resume),
- context.getDrawable(R.drawable.ic_media_play_container),
+ if (Flags.mediaControlsUiUpdate()) {
+ context.getDrawable(R.drawable.ic_media_play_button_container)
+ } else {
+ context.getDrawable(R.drawable.ic_media_play_container)
+ },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index a524db4437a5..587a678c6ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -1197,13 +1197,21 @@ class MediaDataProcessor(
}
private fun getResumeMediaAction(action: Runnable): MediaAction {
+ val iconId =
+ if (Flags.mediaControlsUiUpdate()) {
+ R.drawable.ic_media_play_button
+ } else {
+ R.drawable.ic_media_play
+ }
return MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play)
- .setTint(themeText)
- .loadDrawable(context),
+ Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
context.getString(R.string.controls_media_resume),
- context.getDrawable(R.drawable.ic_media_play_container),
+ if (Flags.mediaControlsUiUpdate()) {
+ context.getDrawable(R.drawable.ic_media_play_button_container)
+ } else {
+ context.getDrawable(R.drawable.ic_media_play_container)
+ },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0ada931aea43..a9732b8e7d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -28,7 +28,6 @@ import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.CheckBox;
import android.widget.TextView;
@@ -159,12 +158,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
&& !mController.hasAdjustVolumeUserRestriction()) {
setUpDeviceIcon(device);
updateProgressBarColor();
- setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+ setSingleLineLayout(device.getName(), false /* showSeekBar*/,
true /* showProgressBar */, false /* showCheckBox */,
false /* showEndTouchArea */);
} else {
setUpDeviceIcon(device);
- setSingleLineLayout(getItemTitle(device));
+ setSingleLineLayout(device.getName());
}
} else {
// Set different layout for each device
@@ -173,7 +172,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateUnmutedVolumeIcon(device);
mCurrentActivePosition = position;
updateFullItemClickListener(v -> onItemClick(v, device));
- setSingleLineLayout(getItemTitle(device));
+ setSingleLineLayout(device.getName());
initFakeActiveDevice(device);
} else if (device.hasSubtext()) {
boolean isActiveWithOngoingSession =
@@ -189,10 +188,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateEndClickAreaAsSessionEditing(device,
isHost ? R.drawable.media_output_status_edit_session
: R.drawable.ic_sound_bars_anim);
- setTwoLineLayout(device, null /* title */, true /* bFocused */,
+ setTwoLineLayout(device.getName() /* title */,
true /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */, false /* showStatus */,
- true /* showEndTouchArea */, false /* isFakeActive */);
+ true /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
} else {
if (currentlyConnected) {
@@ -214,24 +213,23 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateTwoLineLayoutContentAlpha(
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
- setTwoLineLayout(device, currentlyConnected /* bFocused */,
+ setTwoLineLayout(device.getName(),
currentlyConnected /* showSeekBar */,
false /* showProgressBar */, true /* showSubtitle */,
- deviceStatusIcon != null /* showStatus */,
- false /* isFakeActive */);
+ deviceStatusIcon != null /* showStatus */);
}
} else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setUpDeviceIcon(device);
updateConnectionFailedStatusIcon();
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
updateFullItemClickListener(v -> onItemClick(v, device));
- setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */,
+ setTwoLineLayout(device.getName(), false /* showSeekBar */,
false /* showProgressBar */, true /* showSubtitle */,
- true /* showStatus */, false /*isFakeActive*/);
+ true /* showStatus */);
} else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
setUpDeviceIcon(device);
updateProgressBarColor();
- setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+ setSingleLineLayout(device.getName(), false /* showSeekBar*/,
true /* showProgressBar */, false /* showCheckBox */,
false /* showEndTouchArea */);
} else if (mController.getSelectedMediaDevice().size() > 1
@@ -244,7 +242,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateEndClickArea(device, isDeviceDeselectable);
disableFocusPropertyForView(mContainerLayout);
setUpContentDescriptionForView(mSeekBar, device);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ setSingleLineLayout(device.getName(), true /* showSeekBar */,
false /* showProgressBar */, true /* showCheckBox */,
true /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -256,7 +254,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
// mark as disconnected and set special click listener
setUpDeviceIcon(device);
updateFullItemClickListener(v -> cancelMuteAwaitConnection());
- setSingleLineLayout(getItemTitle(device));
+ setSingleLineLayout(device.getName());
} else if (device.hasOngoingSession()) {
mCurrentActivePosition = position;
updateUnmutedVolumeIcon(device);
@@ -264,7 +262,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
? R.drawable.media_output_status_edit_session
: R.drawable.ic_sound_bars_anim);
mEndClickIcon.setVisibility(View.VISIBLE);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ setSingleLineLayout(device.getName(), true /* showSeekBar */,
false /* showProgressBar */, false /* showCheckBox */,
true /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -279,7 +277,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateEndClickArea(device, isDeviceDeselectable);
disableFocusPropertyForView(mContainerLayout);
setUpContentDescriptionForView(mSeekBar, device);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ setSingleLineLayout(device.getName(), true /* showSeekBar */,
false /* showProgressBar */, true /* showCheckBox */,
true /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -288,7 +286,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
disableFocusPropertyForView(mContainerLayout);
setUpContentDescriptionForView(mSeekBar, device);
mCurrentActivePosition = position;
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ setSingleLineLayout(device.getName(), true /* showSeekBar */,
false /* showProgressBar */, false /* showCheckBox */,
false /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -299,12 +297,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateGroupableCheckBox(false, true, device);
updateEndClickArea(device, true);
updateFullItemClickListener(v -> onItemClick(v, device));
- setSingleLineLayout(getItemTitle(device), false /* showSeekBar */,
+ setSingleLineLayout(device.getName(), false /* showSeekBar */,
false /* showProgressBar */, true /* showCheckBox */,
true /* showEndTouchArea */);
} else {
setUpDeviceIcon(device);
- setSingleLineLayout(getItemTitle(device));
+ setSingleLineLayout(device.getName());
Drawable deviceStatusIcon =
device.hasOngoingSession() ? mContext.getDrawable(
R.drawable.ic_sound_bars_anim)
@@ -427,8 +425,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mTitleIcon.setImageDrawable(addDrawable);
mTitleIcon.setImageTintList(
ColorStateList.valueOf(mController.getColorItemContent()));
- mIconAreaLayout.setBackgroundTintList(
- ColorStateList.valueOf(mController.getColorItemBackground()));
mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
}
@@ -494,13 +490,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
== MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
? R.string.accessibility_bluetooth_name
: R.string.accessibility_cast_name, device.getName()));
- view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- host.setOnClickListener(null);
- }
- });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 1a2238cfbc9e..4958b223a2f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -23,7 +23,6 @@ import android.animation.ValueAnimator;
import android.app.WallpaperColors;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Typeface;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
@@ -95,10 +94,6 @@ public abstract class MediaOutputBaseAdapter extends
mController.setCurrentColorScheme(wallpaperColors, isDarkTheme);
}
- CharSequence getItemTitle(MediaDevice device) {
- return device.getName();
- }
-
boolean isCurrentlyConnected(MediaDevice device) {
return TextUtils.equals(device.getId(),
mController.getCurrentConnectedMediaDevice().getId())
@@ -188,6 +183,7 @@ public abstract class MediaOutputBaseAdapter extends
mSubTitleText.setSelected(true);
mTwoLineTitleText.setTextColor(mController.getColorItemContent());
mVolumeValueText.setTextColor(mController.getColorItemContent());
+ mIconAreaLayout.setBackground(null);
mSeekBar.setProgressTintList(
ColorStateList.valueOf(mController.getColorSeekbarProgress()));
}
@@ -216,10 +212,6 @@ public abstract class MediaOutputBaseAdapter extends
mItemLayout.setBackgroundTintList(
ColorStateList.valueOf(isActive ? mController.getColorConnectedItemBackground()
: mController.getColorItemBackground()));
- mIconAreaLayout.setBackgroundTintList(
- ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
- : showProgressBar ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground()));
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSeekBar.setAlpha(1);
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
@@ -236,16 +228,14 @@ public abstract class MediaOutputBaseAdapter extends
: mController.getItemMarginEndDefault();
}
- void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
- boolean showProgressBar, boolean showSubtitle, boolean showStatus,
- boolean isFakeActive) {
- setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
- showStatus, false, isFakeActive);
+ void setTwoLineLayout(CharSequence title, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle, boolean showStatus) {
+ setTwoLineLayout(title, showSeekBar, showProgressBar, showSubtitle, showStatus, false);
}
- void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+ void setTwoLineLayout(CharSequence title,
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
- boolean showStatus , boolean showEndTouchArea, boolean isFakeActive) {
+ boolean showStatus , boolean showEndTouchArea) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
@@ -253,17 +243,12 @@ public abstract class MediaOutputBaseAdapter extends
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
final Drawable backgroundDrawable;
backgroundDrawable = mContext.getDrawable(
- showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active
+ showSeekBar ? R.drawable.media_output_item_background_active
: R.drawable.media_output_item_background).mutate();
mItemLayout.setBackgroundTintList(ColorStateList.valueOf(
- showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground()
+ showSeekBar ? mController.getColorConnectedItemBackground()
: mController.getColorItemBackground()
));
- mIconAreaLayout.setBackgroundTintList(
- ColorStateList.valueOf(showProgressBar || isFakeActive
- ? mController.getColorConnectedItemBackground()
- : showSeekBar ? mController.getColorSeekbarProgress()
- : mController.getColorItemBackground()));
if (showSeekBar) {
updateSeekbarProgressBackground();
}
@@ -277,12 +262,7 @@ public abstract class MediaOutputBaseAdapter extends
mItemLayout.setBackground(backgroundDrawable);
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
- mTwoLineTitleText.setTranslationY(0);
- mTwoLineTitleText.setText(device == null ? title : getItemTitle(device));
- mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
- bFocused ? com.android.internal.R.string.config_headlineFontFamilyMedium
- : com.android.internal.R.string.config_headlineFontFamily),
- Typeface.NORMAL));
+ mTwoLineTitleText.setText(title);
}
void updateSeekbarProgressBackground() {
@@ -443,8 +423,7 @@ public abstract class MediaOutputBaseAdapter extends
mItemLayout.setBackground(backgroundDrawable);
mItemLayout.setBackgroundTintList(
ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
- mIconAreaLayout.setBackgroundTintList(
- ColorStateList.valueOf(mController.getColorConnectedItemBackground()));
+ mIconAreaLayout.setBackground(null);
}
private void initAnimator() {
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 67fe0e981b09..1a5e605c96f8 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -79,7 +79,8 @@ public class SysUiState implements Dumpable {
/** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
- final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag);
+ final Boolean overrideOrNull = mSceneContainerPlugin != null
+ ? mSceneContainerPlugin.flagValueOverride(flag) : null;
if (overrideOrNull != null && enabled != overrideOrNull) {
if (DEBUG) {
Log.d(TAG, "setFlag for flag " + flag + " and value " + enabled + " overridden to "
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 0de8c40bddaa..1807847e3f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -77,7 +77,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.Flags;
@@ -115,7 +115,7 @@ public final class NavBarHelper implements
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener,
AccessibilityGestureTargetsObserver.TargetsChangedListener,
- OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ LauncherProxyService.LauncherProxyListener, NavigationModeController.ModeChangedListener,
Dumpable, CommandQueue.Callbacks, ConfigurationController.ConfigurationListener {
private static final String TAG = NavBarHelper.class.getSimpleName();
@@ -199,7 +199,7 @@ public final class NavBarHelper implements
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
AccessibilityGestureTargetsObserver accessibilityGestureTargetsObserver,
SystemActions systemActions,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
KeyguardStateController keyguardStateController,
@@ -240,7 +240,7 @@ public final class NavBarHelper implements
mNavBarMode = navigationModeController.addListener(this);
mCommandQueue.addCallback(this);
configurationController.addCallback(this);
- overviewProxyService.addCallback(this);
+ launcherProxyService.addCallback(this);
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 645bd0b4b441..ebda3765cf90 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -52,7 +52,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.views.NavigationBar;
import com.android.systemui.navigationbar.views.NavigationBarView;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -115,7 +115,7 @@ public class NavigationBarControllerImpl implements
@Inject
public NavigationBarControllerImpl(Context context,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
NavigationModeController navigationModeController,
SysUiState sysUiFlagsContainer,
CommandQueue commandQueue,
@@ -145,7 +145,7 @@ public class NavigationBarControllerImpl implements
mNavMode = navigationModeController.addListener(this);
mNavBarHelper = navBarHelper;
mTaskbarDelegate = taskbarDelegate;
- mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
+ mTaskbarDelegate.setDependencies(commandQueue, launcherProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideControllerStore.forDisplay(mContext.getDisplayId()),
lightBarController, pipOptional, backAnimation.orElse(null),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index c35a5a96b98c..9d8943052b38 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -70,7 +70,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
@@ -96,7 +96,7 @@ import javax.inject.Inject;
/** */
@SysUISingleton
public class TaskbarDelegate implements CommandQueue.Callbacks,
- OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ LauncherProxyService.LauncherProxyListener, NavigationModeController.ModeChangedListener,
Dumpable {
private static final String TAG = TaskbarDelegate.class.getSimpleName();
@@ -104,7 +104,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final LightBarTransitionsController.Factory mLightBarTransitionsControllerFactory;
private boolean mInitialized;
private CommandQueue mCommandQueue;
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
@@ -210,7 +210,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
public void setDependencies(CommandQueue commandQueue,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
@@ -222,7 +222,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
DisplayTracker displayTracker) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
@@ -240,12 +240,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onDisplayReady(int displayId) {
CommandQueue.Callbacks.super.onDisplayReady(displayId);
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onDisplayReady(displayId);
+ mLauncherProxyService.getProxy().onDisplayReady(displayId);
} catch (RemoteException e) {
Log.e(TAG, "onDisplayReady() failed", e);
}
@@ -254,12 +254,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onDisplayRemoved(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onDisplayRemoved(displayId);
+ mLauncherProxyService.getProxy().onDisplayRemoved(displayId);
} catch (RemoteException e) {
Log.e(TAG, "onDisplayRemoved() failed", e);
}
@@ -268,12 +268,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onDisplayRemoveSystemDecorations(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoveSystemDecorations(displayId);
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onDisplayRemoveSystemDecorations(displayId);
+ mLauncherProxyService.getProxy().onDisplayRemoveSystemDecorations(displayId);
} catch (RemoteException e) {
Log.e(TAG, "onDisplaySystemDecorationsRemoved() failed", e);
}
@@ -287,7 +287,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void applyDarkIntensity(float darkIntensity) {
mBgHandler.post(() -> {
- mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+ mLauncherProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
});
}
@@ -309,7 +309,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mDefaultDisplayId = displayId;
parseCurrentSysuiState();
mCommandQueue.addCallback(this);
- mOverviewProxyService.addCallback(this);
+ mLauncherProxyService.addCallback(this);
onNavigationModeChanged(mNavigationModeController.addListener(this));
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
// Initialize component callback
@@ -334,7 +334,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
return;
}
mCommandQueue.removeCallback(this);
- mOverviewProxyService.removeCallback(this);
+ mLauncherProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mScreenPinningNotify = null;
@@ -401,43 +401,43 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onTransitionModeUpdated(barMode, checkBarModes);
+ mLauncherProxyService.getProxy().onTransitionModeUpdated(barMode, checkBarModes);
} catch (RemoteException e) {
Log.e(TAG, "onTransitionModeUpdated() failed, barMode: " + barMode, e);
}
}
void checkNavBarModes(int displayId) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().checkNavBarModes(displayId);
+ mLauncherProxyService.getProxy().checkNavBarModes(displayId);
} catch (RemoteException e) {
Log.e(TAG, "checkNavBarModes() failed", e);
}
}
void finishBarAnimations(int displayId) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().finishBarAnimations(displayId);
+ mLauncherProxyService.getProxy().finishBarAnimations(displayId);
} catch (RemoteException e) {
Log.e(TAG, "finishBarAnimations() failed", e);
}
}
void touchAutoDim(int displayId) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
@@ -445,31 +445,31 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
int state = mStatusBarStateController.getState();
boolean shouldReset =
state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED;
- mOverviewProxyService.getProxy().touchAutoDim(displayId, shouldReset);
+ mLauncherProxyService.getProxy().touchAutoDim(displayId, shouldReset);
} catch (RemoteException e) {
Log.e(TAG, "touchAutoDim() failed", e);
}
}
void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().transitionTo(displayId, barMode, animate);
+ mLauncherProxyService.getProxy().transitionTo(displayId, barMode, animate);
} catch (RemoteException e) {
Log.e(TAG, "transitionTo() failed, barMode: " + barMode, e);
}
}
private void updateAssistantAvailability(boolean assistantAvailable,
boolean longPressHomeEnabled) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable,
+ mLauncherProxyService.getProxy().onAssistantAvailable(assistantAvailable,
longPressHomeEnabled);
} catch (RemoteException e) {
Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
@@ -477,24 +477,24 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
private void updateWallpaperVisible(int displayId, boolean visible) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().updateWallpaperVisibility(displayId, visible);
+ mLauncherProxyService.getProxy().updateWallpaperVisibility(displayId, visible);
} catch (RemoteException e) {
Log.e(TAG, "updateWallpaperVisibility() failed, visible: " + visible, e);
}
}
private void appTransitionPending(boolean pending) {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().appTransitionPending(pending);
+ mLauncherProxyService.getProxy().appTransitionPending(pending);
} catch (RemoteException e) {
Log.e(TAG, "appTransitionPending() failed, pending: " + pending, e);
}
@@ -528,14 +528,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onRotationProposal(int rotation, boolean isValid) {
- mOverviewProxyService.onRotationProposal(rotation, isValid);
+ mLauncherProxyService.onRotationProposal(rotation, isValid);
}
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
mDisabledFlags = state1;
updateSysuiFlags();
- mOverviewProxyService.disable(displayId, state1, state2, animate);
+ mLauncherProxyService.disable(displayId, state1, state2, animate);
}
@Override
@@ -543,7 +543,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
@InsetsType int requestedVisibleTypes, String packageName,
LetterboxDetails[] letterboxDetails) {
- mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+ mLauncherProxyService.onSystemBarAttributesChanged(displayId, behavior);
boolean nbModeChanged = false;
if (mAppearance != appearance) {
mAppearance = appearance;
@@ -596,12 +596,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void toggleTaskbar() {
- if (mOverviewProxyService.getProxy() == null) {
+ if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().onTaskbarToggled();
+ mLauncherProxyService.getProxy().onTaskbarToggled();
} catch (RemoteException e) {
Log.e(TAG, "onTaskbarToggled() failed", e);
}
@@ -673,7 +673,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
- mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
+ mLauncherProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 1c94f56f0942..f44c2c01951c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -85,7 +85,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -155,8 +155,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
};
- private OverviewProxyService.OverviewProxyListener mQuickSwitchListener =
- new OverviewProxyService.OverviewProxyListener() {
+ private LauncherProxyService.LauncherProxyListener mQuickSwitchListener =
+ new LauncherProxyService.LauncherProxyListener() {
@Override
public void onPrioritizedRotation(@Surface.Rotation int rotation) {
mStartingQuickstepRotation = rotation;
@@ -197,7 +197,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final Context mContext;
private final UserTracker mUserTracker;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final SysUiState mSysUiState;
private Runnable mStateChangeCallback;
private Consumer<Boolean> mButtonForcedVisibleCallback;
@@ -332,7 +332,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
: SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
if (!mInRejectedExclusion) {
// Log successful back gesture to contextual edu stats
- mOverviewProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
+ mLauncherProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
GestureType.BACK);
}
}
@@ -441,7 +441,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
@AssistedInject
EdgeBackGestureHandler(
@Assisted Context context,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
SysUiState sysUiState,
PluginManager pluginManager,
@BackPanelUiThread UiThreadContext uiThreadContext,
@@ -468,7 +468,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mBackgroundExecutor = backgroundExecutor;
mBgHandler = bgHandler;
mUserTracker = userTracker;
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
mNavigationModeController = navigationModeController;
@@ -620,7 +620,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
*/
public void onNavBarAttached() {
mIsAttached = true;
- mOverviewProxyService.addCallback(mQuickSwitchListener);
+ mLauncherProxyService.addCallback(mQuickSwitchListener);
mSysUiState.addCallback(mSysUiStateCallback);
mInputManager.registerInputDeviceListener(mInputDeviceListener, mBgHandler);
int[] inputDevices = mInputManager.getInputDeviceIds();
@@ -636,7 +636,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
*/
public void onNavBarDetached() {
mIsAttached = false;
- mOverviewProxyService.removeCallback(mQuickSwitchListener);
+ mLauncherProxyService.removeCallback(mQuickSwitchListener);
mSysUiState.removeCallback(mSysUiStateCallback);
mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
mTrackpadsConnected.clear();
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 692b1f341c94..f95f45906b23 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -36,7 +36,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
-import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.systemui.shared.rotation.RotationButtonController.DEBUG_ROTATION;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -131,7 +131,7 @@ import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
import com.android.systemui.navigationbar.views.buttons.NavBarButtonClickLogger;
import com.android.systemui.navigationbar.views.buttons.NavbarOrientationTrackingLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
@@ -212,7 +212,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final ShadeViewController mShadeViewController;
private final PanelExpansionInteractor mPanelExpansionInteractor;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final NavigationModeController mNavigationModeController;
private final UserTracker mUserTracker;
private final CommandQueue mCommandQueue;
@@ -283,7 +283,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
* gesture to indicate to them that they can continue in that orientation without having to
* rotate the phone
* The secondary handle will show when we get
- * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the
+ * {@link LauncherProxyListener#notifyPrioritizedRotation(int)} callback with the
* original handle hidden and we'll flip the visibilities once the
* {@link #mTasksFrozenListener} fires
*/
@@ -387,12 +387,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
- private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ private final LauncherProxyListener mLauncherProxyListener = new LauncherProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
- mView.onOverviewProxyConnectionChange(
- mOverviewProxyService.isEnabled());
- mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
+ mView.onLauncherProxyConnectionChange(
+ mLauncherProxyService.isEnabled());
+ mView.setShouldShowSwipeUpUi(mLauncherProxyService.shouldShowSwipeUpUI());
updateScreenPinningGestures();
}
@@ -565,7 +565,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
MetricsLogger metricsLogger,
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
NavigationModeController navigationModeController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -618,7 +618,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mShadeViewController = shadeViewController;
mPanelExpansionInteractor = panelExpansionInteractor;
mNotificationRemoteInputManager = notificationRemoteInputManager;
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mNavigationModeController = navigationModeController;
mUserTracker = userTracker;
mCommandQueue = commandQueue;
@@ -827,7 +827,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
setNavBarMode(mNavBarMode);
repositionNavigationBar(mCurrentRotation);
mView.setUpdateActiveTouchRegionsCallback(
- () -> mOverviewProxyService.onActiveNavBarRegionChanges(
+ () -> mLauncherProxyService.onActiveNavBarRegionChanges(
getButtonLocations(true /* inScreen */, true /* useNearestRegion */)));
mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
@@ -843,7 +843,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
notifyNavigationBarScreenOn();
- mOverviewProxyService.addCallback(mOverviewProxyListener);
+ mLauncherProxyService.addCallback(mLauncherProxyListener);
updateSystemUiStateFlags();
// Currently there is no accelerometer sensor on non-default display.
@@ -881,7 +881,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
public void onViewDetached() {
mView.setUpdateActiveTouchRegionsCallback(null);
getBarTransitions().destroy();
- mOverviewProxyService.removeCallback(mOverviewProxyListener);
+ mLauncherProxyService.removeCallback(mLauncherProxyListener);
mUserTracker.removeCallback(mUserChangedCallback);
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
if (mOrientationHandle != null) {
@@ -1699,9 +1699,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private void updateAssistantEntrypoints(boolean assistantAvailable,
boolean longPressHomeEnabled) {
- if (mOverviewProxyService.getProxy() != null) {
+ if (mLauncherProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable,
+ mLauncherProxyService.getProxy().onAssistantAvailable(assistantAvailable,
longPressHomeEnabled);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
@@ -2108,7 +2108,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
}
- mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
+ mView.setShouldShowSwipeUpUi(mLauncherProxyService.shouldShowSwipeUpUI());
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
index 96b730c08397..2c5a9c84645b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarInflaterView.java
@@ -41,7 +41,7 @@ import com.android.systemui.navigationbar.views.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
import com.android.systemui.navigationbar.views.buttons.ReverseLinearLayout;
import com.android.systemui.navigationbar.views.buttons.ReverseLinearLayout.ReverseRelativeLayout;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.res.R;
import com.android.systemui.shared.system.QuickStepContract;
@@ -117,13 +117,13 @@ public class NavigationBarInflaterView extends FrameLayout {
private boolean mIsVertical;
private boolean mAlternativeOrder;
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
public NavigationBarInflaterView(Context context, AttributeSet attrs) {
super(context, attrs);
createInflaters();
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mLauncherProxyService = Dependency.get(LauncherProxyService.class);
mListener = new Listener(this);
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(mListener);
}
@@ -159,7 +159,7 @@ public class NavigationBarInflaterView extends FrameLayout {
protected String getDefaultLayout() {
final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)
? R.string.config_navBarLayoutHandle
- : mOverviewProxyService.shouldShowSwipeUpUI()
+ : mLauncherProxyService.shouldShowSwipeUpUI()
? R.string.config_navBarLayoutQuickstep
: R.string.config_navBarLayout;
return getContext().getString(defaultResource);
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 21366847bc84..36cb8fa374b0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -180,7 +180,7 @@ public class NavigationBarView extends FrameLayout {
*/
private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons();
private Gefingerpoken mTouchHandler;
- private boolean mOverviewProxyEnabled;
+ private boolean mLauncherProxyEnabled;
private boolean mShowSwipeUpUi;
private UpdateActiveTouchRegionsCallback mUpdateActiveTouchRegionsCallback;
@@ -642,7 +642,7 @@ public class NavigationBarView extends FrameLayout {
// When screen pinning, don't hide back and home when connected service or back and
// recents buttons when disconnected from launcher service in screen pinning mode,
// as they are used for exiting.
- if (mOverviewProxyEnabled) {
+ if (mLauncherProxyEnabled) {
// Force disable recents when not in legacy mode
disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
@@ -764,8 +764,8 @@ public class NavigationBarView extends FrameLayout {
}
}
- void onOverviewProxyConnectionChange(boolean enabled) {
- mOverviewProxyEnabled = enabled;
+ void onLauncherProxyConnectionChange(boolean enabled) {
+ mLauncherProxyEnabled = enabled;
}
void setShouldShowSwipeUpUi(boolean showSwipeUpUi) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
index 111a2d43f881..32a03e5b10e9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/KeyButtonView.java
@@ -61,7 +61,7 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.res.R;
import com.android.systemui.shared.navigationbar.KeyButtonRipple;
import com.android.systemui.shared.system.QuickStepContract;
@@ -82,7 +82,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
@VisibleForTesting boolean mLongClicked;
private OnClickListener mOnClickListener;
private final KeyButtonRipple mRipple;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final InputManagerGlobal mInputManagerGlobal;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
@@ -181,7 +181,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mRipple = new KeyButtonRipple(context, this, R.dimen.key_button_ripple_max_width);
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ mLauncherProxyService = Dependency.get(LauncherProxyService.class);
mInputManagerGlobal = manager;
setBackground(mRipple);
setWillNotDraw(false);
@@ -282,7 +282,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
@Override
public boolean onTouchEvent(MotionEvent ev) {
- final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
+ final boolean showSwipeUI = mLauncherProxyService.shouldShowSwipeUpUI();
final int action = ev.getAction();
int x, y;
if (action == MotionEvent.ACTION_DOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 195b0cebe2eb..1b9251061f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -21,7 +21,8 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -38,7 +39,10 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
Swipe.Up to HideOverlay(Overlays.NotificationsShade),
Back to HideOverlay(Overlays.NotificationsShade),
Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
- ReplaceByOverlay(Overlays.QuickSettingsShade),
+ ShowOverlay(
+ Overlays.QuickSettingsShade,
+ hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade),
+ ),
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index afb852ae824c..c8f7be6d80b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.systemui.Flags.quickSettingsVisualHapticsLongpress;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -364,12 +363,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
}
private void addTile(final QSTile tile, boolean collapsedView) {
- QSLongPressEffect longPressEffect;
- if (quickSettingsVisualHapticsLongpress()) {
- longPressEffect = mLongPressEffectProvider.get();
- } else {
- longPressEffect = null;
- }
+ QSLongPressEffect longPressEffect = mLongPressEffectProvider.get();
final QSTileViewImpl tileView = new QSTileViewImpl(
getContext(), collapsedView, longPressEffect);
final TileRecord r = new TileRecord(tile, tileView);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 6d3e5d07c251..b1f99cccff70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -61,6 +61,7 @@ constructor(
private val internetDialogManager: InternetDialogManager,
private val wifiStateWorker: WifiStateWorker,
private val accessPointController: AccessPointController,
+ private val internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -107,7 +108,7 @@ constructor(
}
override fun getDetailsViewModel(): TileDetailsViewModel {
- return InternetDetailsViewModel { longClick(null) }
+ return internetDetailsViewModelFactory.create { longClick(null) }
}
override fun handleSecondaryClick(expandable: Expandable?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
index c64532a2c4ba..733159e285e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -57,10 +57,8 @@ import com.android.settingslib.satellite.SatelliteDialogUtils.mayStartSatelliteW
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
import com.android.systemui.Prefs
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan
-import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -75,11 +73,7 @@ import kotlinx.coroutines.Job
/**
* View content for the Internet tile details that handles all UI interactions and state management.
- *
- * @param internetDialog non-null if the details should be shown as part of a dialog and null
- * otherwise.
*/
-// TODO: b/377388104 Make this content for details view only.
class InternetDetailsContentManager
@AssistedInject
constructor(
@@ -88,9 +82,7 @@ constructor(
@Assisted(CAN_CONFIG_WIFI) private val canConfigWifi: Boolean,
@Assisted private val coroutineScope: CoroutineScope,
@Assisted private var context: Context,
- @Assisted private var internetDialog: SystemUIDialog?,
private val uiEventLogger: UiEventLogger,
- private val dialogTransitionAnimator: DialogTransitionAnimator,
@Main private val handler: Handler,
@Background private val backgroundExecutor: Executor,
private val keyguard: KeyguardStateController,
@@ -104,8 +96,6 @@ constructor(
// UI Components
private lateinit var contentView: View
- private lateinit var internetDialogTitleView: TextView
- private lateinit var internetDialogSubTitleView: TextView
private lateinit var divider: View
private lateinit var progressBar: ProgressBar
private lateinit var ethernetLayout: LinearLayout
@@ -132,7 +122,6 @@ constructor(
private lateinit var shareWifiButton: Button
private lateinit var airplaneModeButton: Button
private var alertDialog: AlertDialog? = null
- private lateinit var doneButton: Button
private val canChangeWifiState =
WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
@@ -153,7 +142,6 @@ constructor(
@Assisted(CAN_CONFIG_WIFI) canConfigWifi: Boolean,
coroutineScope: CoroutineScope,
context: Context,
- internetDialog: SystemUIDialog?,
): InternetDetailsContentManager
}
@@ -209,8 +197,6 @@ constructor(
}
// Network layouts
- internetDialogTitleView = contentView.requireViewById(R.id.internet_dialog_title)
- internetDialogSubTitleView = contentView.requireViewById(R.id.internet_dialog_subtitle)
divider = contentView.requireViewById(R.id.divider)
progressBar = contentView.requireViewById(R.id.wifi_searching_progress)
@@ -219,15 +205,6 @@ constructor(
setMobileLayout()
ethernetLayout = contentView.requireViewById(R.id.ethernet_layout)
- // Done button is only visible for the dialog view
- doneButton = contentView.requireViewById(R.id.done_button)
- if (internetDialog == null) {
- doneButton.visibility = View.GONE
- } else {
- // Set done button if qs details view is not enabled.
- doneButton.setOnClickListener { internetDialog!!.dismiss() }
- }
-
// Share WiFi
shareWifiButton = contentView.requireViewById(R.id.share_wifi_button)
shareWifiButton.setOnClickListener { view ->
@@ -251,6 +228,17 @@ constructor(
// Background drawables
backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
backgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect)
+
+ // Done button is only visible for the dialog view
+ contentView.findViewById<Button>(R.id.done_button).apply { visibility = View.GONE }
+
+ // Title and subtitle will be added in the `TileDetails`
+ contentView.findViewById<TextView>(R.id.internet_dialog_title).apply {
+ visibility = View.GONE
+ }
+ contentView.findViewById<TextView>(R.id.internet_dialog_subtitle).apply {
+ visibility = View.GONE
+ }
}
private fun setWifiLayout() {
@@ -336,21 +324,19 @@ constructor(
}
}
- private fun getDialogTitleText(): CharSequence {
- return internetDetailsContentController.getDialogTitleText()
+ fun getTitleText(): String {
+ return internetDetailsContentController.getDialogTitleText().toString()
+ }
+
+ fun getSubtitleText(): String {
+ return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
}
private fun updateDetailsUI(internetContent: InternetContent) {
if (DEBUG) {
Log.d(TAG, "updateDetailsUI ")
}
- if (QsDetailedView.isEnabled) {
- internetDialogTitleView.visibility = View.GONE
- internetDialogSubTitleView.visibility = View.GONE
- } else {
- internetDialogTitleView.text = internetContent.internetDialogTitleString
- internetDialogSubTitleView.text = internetContent.internetDialogSubTitle
- }
+
airplaneModeButton.visibility =
if (internetContent.isAirplaneModeEnabled) View.VISIBLE else View.GONE
@@ -361,17 +347,11 @@ constructor(
private fun getStartingInternetContent(): InternetContent {
return InternetContent(
- internetDialogTitleString = getDialogTitleText(),
- internetDialogSubTitle = getSubtitleText(),
isWifiEnabled = internetDetailsContentController.isWifiEnabled,
isDeviceLocked = internetDetailsContentController.isDeviceLocked,
)
}
- private fun getSubtitleText(): String {
- return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
- }
-
@VisibleForTesting
internal fun hideWifiViews() {
setProgressBarVisible(false)
@@ -393,7 +373,6 @@ constructor(
progressBar.visibility = if (visible) View.VISIBLE else View.GONE
progressBar.isIndeterminate = visible
divider.visibility = if (visible) View.GONE else View.VISIBLE
- internetDialogSubTitleView.text = getSubtitleText()
}
private fun showTurnOffAutoDataSwitchDialog(subId: Int) {
@@ -418,12 +397,7 @@ constructor(
SystemUIDialog.setShowForAllUsers(alertDialog, true)
SystemUIDialog.registerDismissListener(alertDialog)
SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
- if (QsDetailedView.isEnabled) {
- alertDialog!!.show()
- } else {
- dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
- Log.e(TAG, "Internet dialog is shown with the refactor code")
- }
+ alertDialog!!.show()
}
private fun shouldShowMobileDialog(): Boolean {
@@ -466,11 +440,8 @@ constructor(
SystemUIDialog.setShowForAllUsers(alertDialog, true)
SystemUIDialog.registerDismissListener(alertDialog)
SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
- if (QsDetailedView.isEnabled) {
- alertDialog!!.show()
- } else {
- dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
- }
+
+ alertDialog!!.show()
}
private fun onClickConnectedWifi(view: View?) {
@@ -803,7 +774,6 @@ constructor(
secondaryMobileNetworkLayout?.setOnClickListener(null)
seeAllLayout.setOnClickListener(null)
wifiToggle.setOnCheckedChangeListener(null)
- doneButton.setOnClickListener(null)
shareWifiButton.setOnClickListener(null)
airplaneModeButton.setOnClickListener(null)
internetDetailsContentController.onStop()
@@ -825,8 +795,6 @@ constructor(
private fun getInternetContent(shouldUpdateMobileNetwork: Boolean): InternetContent {
return InternetContent(
shouldUpdateMobileNetwork = shouldUpdateMobileNetwork,
- internetDialogTitleString = getDialogTitleText(),
- internetDialogSubTitle = getSubtitleText(),
activeNetworkIsCellular =
if (shouldUpdateMobileNetwork)
internetDetailsContentController.activeNetworkIsCellular()
@@ -924,10 +892,7 @@ constructor(
if (DEBUG) {
Log.d(TAG, "dismissDialog")
}
- if (internetDialog != null) {
- internetDialog!!.dismiss()
- internetDialog = null
- }
+ // TODO: b/377388104 Close details view
}
override fun onAccessPointsChanged(
@@ -967,8 +932,6 @@ constructor(
@VisibleForTesting
data class InternetContent(
- val internetDialogTitleString: CharSequence,
- val internetDialogSubTitle: CharSequence,
val isAirplaneModeEnabled: Boolean = false,
val hasEthernet: Boolean = false,
val shouldUpdateMobileNetwork: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
index f239a179d79a..df4dddbca9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
@@ -16,44 +16,93 @@
package com.android.systemui.qs.tiles.dialog
+import android.util.Log
import android.view.LayoutInflater
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.res.R
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-class InternetDetailsViewModel(
- onLongClick: () -> Unit,
+class InternetDetailsViewModel
+@AssistedInject
+constructor(
+ private val accessPointController: AccessPointController,
+ private val contentManagerFactory: InternetDetailsContentManager.Factory,
+ @Assisted private val onLongClick: () -> Unit,
) : TileDetailsViewModel() {
- private val _onLongClick = onLongClick
+ private lateinit var internetDetailsContentManager: InternetDetailsContentManager
@Composable
override fun GetContentView() {
+ val coroutineScope = rememberCoroutineScope()
+ val context = LocalContext.current
+
+ internetDetailsContentManager = remember {
+ contentManagerFactory.create(
+ canConfigMobileData = accessPointController.canConfigMobileData(),
+ canConfigWifi = accessPointController.canConfigWifi(),
+ coroutineScope = coroutineScope,
+ context = context,
+ )
+ }
AndroidView(
modifier = Modifier.fillMaxWidth().fillMaxHeight(),
factory = { context ->
- // Inflate with the existing dialog xml layout
- LayoutInflater.from(context)
- .inflate(R.layout.internet_connectivity_dialog, null)
- // TODO: b/377388104 - Implement the internet details view
+ // Inflate with the existing dialog xml layout and bind it with the manager
+ val view =
+ LayoutInflater.from(context)
+ .inflate(R.layout.internet_connectivity_dialog, null)
+ internetDetailsContentManager.bind(view)
+
+ view
+ // TODO: b/377388104 - Polish the internet details view UI
+ },
+ onRelease = {
+ internetDetailsContentManager.unBind()
+ if (DEBUG) {
+ Log.d(TAG, "onRelease")
+ }
},
)
}
override fun clickOnSettingsButton() {
- _onLongClick()
+ onLongClick()
}
override fun getTitle(): String {
+ // TODO: b/377388104 make title and sub title mutable states of string
+ // by internetDetailsContentManager.getTitleText()
+ // TODO: test title change between airplane mode and not airplane mode
// TODO: b/377388104 Update the placeholder text
return "Internet"
}
override fun getSubTitle(): String {
+ // TODO: b/377388104 make title and sub title mutable states of string
+ // by internetDetailsContentManager.getSubtitleText()
+ // TODO: test subtitle change between airplane mode and not airplane mode
// TODO: b/377388104 Update the placeholder text
return "Tab a network to connect"
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(onLongClick: () -> Unit): InternetDetailsViewModel
+ }
+
+ companion object {
+ private const val TAG = "InternetDetailsVModel"
+ private val DEBUG: Boolean = Log.isLoggable(TAG, Log.DEBUG)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index c4f9515b819f..6e2c437b9c16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -43,6 +43,7 @@ constructor(
private val wifiStateWorker: WifiStateWorker,
private val accessPointController: AccessPointController,
private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
) : QSTileUserActionInteractor<InternetTileModel> {
override suspend fun handleInput(input: QSTileInput<InternetTileModel>): Unit =
@@ -70,7 +71,7 @@ constructor(
}
override val detailsViewModel: TileDetailsViewModel =
- InternetDetailsViewModel { handleLongClick(null) }
+ internetDetailsViewModelFactory.create { handleLongClick(null) }
private fun handleLongClick(expandable:Expandable?){
qsTileIntentUserActionHandler.handle(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 000f7f8a7d31..5bc26f50f70f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -21,7 +21,8 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge
@@ -47,7 +48,11 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
}
put(
Swipe.Down(fromSource = SceneContainerEdge.TopLeft),
- ReplaceByOverlay(Overlays.NotificationsShade),
+ ShowOverlay(
+ Overlays.NotificationsShade,
+ hideCurrentOverlays =
+ HideCurrentOverlays.Some(Overlays.QuickSettingsShade),
+ ),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 60c2cca1ae8b..9af4630bf492 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -100,14 +100,14 @@ import com.android.systemui.navigationbar.views.NavigationBar;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.navigationbar.views.buttons.KeyButtonView;
import com.android.systemui.process.ProcessWrapper;
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ILauncherProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -135,16 +135,16 @@ import javax.inject.Inject;
import javax.inject.Provider;
/**
- * Class to send information from overview to launcher with a binder.
+ * Class to send information from SysUI to Launcher with a binder.
*/
@SysUISingleton
-public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
+public class LauncherProxyService implements CallbackController<LauncherProxyListener>,
NavigationModeController.ModeChangedListener, Dumpable {
@VisibleForTesting
static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
- public static final String TAG_OPS = "OverviewProxyService";
+ public static final String TAG_OPS = "LauncherProxyService";
private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
// Max backoff caps at 5 mins
@@ -165,7 +165,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final Runnable mConnectionRunnable = () ->
internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
private final ComponentName mRecentsComponentName;
- private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
+ private final List<LauncherProxyListener> mConnectionCallbacks = new ArrayList<>();
private final Intent mQuickStepIntent;
private final ScreenshotHelper mScreenshotHelper;
private final CommandQueue mCommandQueue;
@@ -179,12 +179,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final BroadcastDispatcher mBroadcastDispatcher;
private final BackAnimation mBackAnimation;
- private IOverviewProxy mOverviewProxy;
+ private ILauncherProxy mLauncherProxy;
private int mConnectionBackoffAttempts;
private boolean mBound;
private boolean mIsEnabled;
- // This is set to false when the overview service is requested to be bound until it is notified
- // that the previous service has been cleaned up in IOverviewProxy#onUnbind(). It is also set to
+ // This is set to false when the launcher service is requested to be bound until it is notified
+ // that the previous service has been cleaned up in ILauncherProxy#onUnbind(). It is also set to
// true after a 1000ms timeout by mDeferredBindAfterTimedOutCleanup.
private boolean mIsPrevServiceCleanedUp = true;
@@ -341,7 +341,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
verifyCallerAndClearCallingIdentityPostMain("updateContextualEduStats",
- () -> mHandler.post(() -> OverviewProxyService.this.updateContextualEduStats(
+ () -> mHandler.post(() -> LauncherProxyService.this.updateContextualEduStats(
isTrackpadGesture, GestureType.valueOf(gestureType))));
}
@@ -504,7 +504,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void onReceive(Context context, Intent intent) {
if (Objects.equals(intent.getAction(), Intent.ACTION_USER_UNLOCKED)) {
if (keyguardPrivateNotifications()) {
- // Start the overview connection to the launcher service
+ // Start the launcher connection to the launcher service
// Connect if user hasn't connected yet
if (getProxy() == null) {
startConnectionToCurrentUser();
@@ -546,14 +546,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
};
- private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
+ private final ServiceConnection mLauncherServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG_OPS, "Overview proxy service connected");
+ Log.d(TAG_OPS, "Launcher proxy service connected");
mConnectionBackoffAttempts = 0;
mHandler.removeCallbacks(mDeferredConnectionCallback);
try {
- service.linkToDeath(mOverviewServiceDeathRcpt, 0);
+ service.linkToDeath(mLauncherServiceDeathRcpt, 0);
} catch (RemoteException e) {
// Failed to link to death (process may have died between binding and connecting),
// just unbind the service for now and retry again
@@ -564,7 +564,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
mCurrentBoundedUserId = mUserTracker.getUserId();
- mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
+ mLauncherProxy = ILauncherProxy.Stub.asInterface(service);
Bundle params = new Bundle();
addInterface(mSysUiProxy, params);
@@ -574,8 +574,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mShellInterface.createExternalInterfaces(params);
try {
- Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
- mOverviewProxy.onInitialize(params);
+ Log.d(TAG_OPS, "LauncherProxyService connected, initializing launcher proxy");
+ mLauncherProxy.onInitialize(params);
} catch (RemoteException e) {
mCurrentBoundedUserId = -1;
Log.e(TAG_OPS, "Failed to call onInitialize()", e);
@@ -614,7 +614,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
// This is the death handler for the binder from the launcher service
- private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+ private final IBinder.DeathRecipient mLauncherServiceDeathRcpt
= this::cleanupAfterDeath;
private final IVoiceInteractionSessionListener mVoiceInteractionSessionListener =
@@ -632,7 +632,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
mContext.getMainExecutor().execute(() ->
- OverviewProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
+ LauncherProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
}
@Override
@@ -652,7 +652,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyService(Context context,
+ public LauncherProxyService(Context context,
@Main Executor mainExecutor,
CommandQueue commandQueue,
ShellInterface shellInterface,
@@ -755,14 +755,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) {
- if (mOverviewProxy != null) {
+ if (mLauncherProxy != null) {
try {
if (DesktopModeStatus.canEnterDesktopMode(mContext)
&& (sysUiState.getFlags()
& SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
return;
}
- mOverviewProxy.enterStageSplitFromRunningApp(leftOrTop);
+ mLauncherProxy.enterStageSplitFromRunningApp(leftOrTop);
} catch (RemoteException e) {
Log.w(TAG_OPS, "Unable to enter stage split from the current running app");
}
@@ -817,12 +817,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private void notifySystemUiStateFlags(@SystemUiStateFlags long flags) {
if (SysUiState.DEBUG) {
- Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
- + mOverviewProxy + " flags=" + flags);
+ Log.d(TAG_OPS, "Notifying sysui state change to launcher service: proxy="
+ + mLauncherProxy + " flags=" + flags);
}
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onSystemUiStateChanged(flags);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onSystemUiStateChanged(flags);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to notify sysui state change", e);
@@ -854,9 +854,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
private void dispatchNavButtonBounds() {
- if (mOverviewProxy != null && mActiveNavBarRegion != null) {
+ if (mLauncherProxy != null && mActiveNavBarRegion != null) {
try {
- mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
+ mLauncherProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
}
@@ -888,7 +888,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// This should not happen, but if any per-user SysUI component has a dependency on OPS,
// then this could get triggered
Log.w(TAG_OPS,
- "Skipping connection to overview service due to non-system foreground user "
+ "Skipping connection to launcher service due to non-system foreground user "
+ "caller");
return;
}
@@ -925,7 +925,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
try {
mBound = mContext.bindServiceAsUser(mQuickStepIntent,
- mOverviewServiceConnection,
+ mLauncherServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
currentUser);
} catch (SecurityException e) {
@@ -954,15 +954,15 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
@Override
- public void addCallback(@NonNull OverviewProxyListener listener) {
+ public void addCallback(@NonNull LauncherProxyListener listener) {
if (!mConnectionCallbacks.contains(listener)) {
mConnectionCallbacks.add(listener);
}
- listener.onConnectionChanged(mOverviewProxy != null);
+ listener.onConnectionChanged(mLauncherProxy != null);
}
@Override
- public void removeCallback(@NonNull OverviewProxyListener listener) {
+ public void removeCallback(@NonNull LauncherProxyListener listener) {
mConnectionCallbacks.remove(listener);
}
@@ -974,21 +974,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
return mIsEnabled;
}
- public IOverviewProxy getProxy() {
- return mOverviewProxy;
+ public ILauncherProxy getProxy() {
+ return mLauncherProxy;
}
private void disconnectFromLauncherService(String disconnectReason) {
Log.d(TAG_OPS, "disconnectFromLauncherService bound?: " + mBound +
- " currentProxy: " + mOverviewProxy + " disconnectReason: " + disconnectReason,
+ " currentProxy: " + mLauncherProxy + " disconnectReason: " + disconnectReason,
new Throwable());
if (mBound) {
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
- mContext.unbindService(mOverviewServiceConnection);
+ mContext.unbindService(mLauncherServiceConnection);
mBound = false;
- if (mOverviewProxy != null) {
+ if (mLauncherProxy != null) {
try {
- mOverviewProxy.onUnbind(new IRemoteCallback.Stub() {
+ mLauncherProxy.onUnbind(new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
// Received Launcher reply, try to bind anew.
@@ -1006,9 +1006,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
- if (mOverviewProxy != null) {
- mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
- mOverviewProxy = null;
+ if (mLauncherProxy != null) {
+ mLauncherProxy.asBinder().unlinkToDeath(mLauncherServiceDeathRcpt, 0);
+ mLauncherProxy = null;
notifyConnectionChanged();
}
}
@@ -1044,7 +1044,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private void notifyConnectionChanged() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
+ mConnectionCallbacks.get(i).onConnectionChanged(mLauncherProxy != null);
}
}
@@ -1095,10 +1095,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void notifyAssistantVisibilityChanged(float visibility) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onAssistantVisibilityChanged(visibility);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onAssistantVisibilityChanged(visibility);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for assistant visibility.");
+ Log.e(TAG_OPS, "Failed to get launcher proxy for assistant visibility.");
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call notifyAssistantVisibilityChanged()", e);
@@ -1148,10 +1148,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void disable(int displayId, int state1, int state2, boolean animate) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.disable(displayId, state1, state2, animate);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.disable(displayId, state1, state2, animate);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
+ Log.e(TAG_OPS, "Failed to get launcher proxy for disable flags.");
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call disable()", e);
@@ -1160,10 +1160,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void onRotationProposal(int rotation, boolean isValid) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onRotationProposal(rotation, isValid);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onRotationProposal(rotation, isValid);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
+ Log.e(TAG_OPS, "Failed to get launcher proxy for proposing rotation.");
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
@@ -1172,10 +1172,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void onSystemBarAttributesChanged(int displayId, int behavior) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onSystemBarAttributesChanged(displayId, behavior);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+ Log.e(TAG_OPS, "Failed to get launcher proxy for system bar attr change.");
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
@@ -1184,10 +1184,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+ Log.e(TAG_OPS, "Failed to get launcher proxy to update nav buttons dark intensity");
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
@@ -1196,10 +1196,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
+ if (mLauncherProxy != null) {
+ mLauncherProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma"
+ Log.e(TAG_OPS, "Failed to get launcher proxy to enable/disable nav bar luma"
+ "sampling");
}
} catch (RemoteException e) {
@@ -1221,7 +1221,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG_OPS + " state:");
- pw.print(" isConnected="); pw.println(mOverviewProxy != null);
+ pw.print(" isConnected="); pw.println(mLauncherProxy != null);
pw.print(" mIsEnabled="); pw.println(isEnabled());
pw.print(" mRecentsComponentName="); pw.println(mRecentsComponentName);
pw.print(" mQuickStepIntent="); pw.println(mQuickStepIntent);
@@ -1237,7 +1237,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mSysUiState.dump(pw, args);
}
- public interface OverviewProxyListener {
+ public interface LauncherProxyListener {
default void onConnectionChanged(boolean isConnected) {}
default void onPrioritizedRotation(@Surface.Rotation int rotation) {}
default void onOverviewShown(boolean fromHome) {}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 21c5ae8aec40..e51b73dd96c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -23,30 +23,30 @@ import android.util.Log;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ILauncherProxy;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
/**
- * An implementation of the Recents interface which proxies to the OverviewProxyService.
+ * An implementation of the Recents interface which proxies to the LauncherProxyService.
*/
@SysUISingleton
public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
private Handler mHandler;
- private final OverviewProxyService mOverviewProxyService;
+ private final LauncherProxyService mLauncherProxyService;
private final ActivityStarter mActivityStarter;
private final KeyguardStateController mKeyguardStateController;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyRecentsImpl(
- OverviewProxyService overviewProxyService,
+ LauncherProxyService launcherProxyService,
ActivityStarter activityStarter,
KeyguardStateController keyguardStateController) {
- mOverviewProxyService = overviewProxyService;
+ mLauncherProxyService = launcherProxyService;
mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
}
@@ -58,10 +58,10 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@Override
public void showRecentApps(boolean triggeredFromAltTab) {
- IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
- if (overviewProxy != null) {
+ ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+ if (launcherProxy != null) {
try {
- overviewProxy.onOverviewShown(triggeredFromAltTab);
+ launcherProxy.onOverviewShown(triggeredFromAltTab);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send overview show event to launcher.", e);
}
@@ -70,10 +70,10 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@Override
public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
- if (overviewProxy != null) {
+ ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+ if (launcherProxy != null) {
try {
- overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
+ launcherProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send overview hide event to launcher.", e);
}
@@ -83,13 +83,13 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@Override
public void toggleRecentApps() {
// If connected to launcher service, let it handle the toggle logic
- IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
- if (overviewProxy != null) {
+ ILauncherProxy launcherProxy = mLauncherProxyService.getProxy();
+ if (launcherProxy != null) {
final Runnable toggleRecents = () -> {
try {
- if (mOverviewProxyService.getProxy() != null) {
- mOverviewProxyService.getProxy().onOverviewToggle();
- mOverviewProxyService.notifyToggleRecentApps();
+ if (mLauncherProxyService.getProxy() != null) {
+ mLauncherProxyService.getProxy().onOverviewToggle();
+ mLauncherProxyService.notifyToggleRecentApps();
}
} catch (RemoteException e) {
Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index a1b4def09ba9..28f5694c3332 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2251,7 +2251,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private boolean isPanelVisibleBecauseOfHeadsUp() {
- boolean headsUpVisible = mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
+ boolean headsUpVisible = (mHeadsUpManager != null && mHeadsUpManager.hasPinnedHeadsUp())
+ || mHeadsUpAnimatingAway;
return headsUpVisible && mBarState == StatusBarState.SHADE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 7299f092640f..cf310dd32613 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -34,8 +34,8 @@ import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.qs.QSContainerController
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.system.QuickStepContract
@@ -57,7 +57,7 @@ class NotificationsQSContainerController
constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
- private val overviewProxyService: OverviewProxyService,
+ private val launcherProxyService: LauncherProxyService,
private val shadeHeaderController: ShadeHeaderController,
private val shadeInteractor: ShadeInteractor,
private val fragmentService: FragmentService,
@@ -85,8 +85,8 @@ constructor(
private var isGestureNavigation = true
private var taskbarVisible = false
- private val taskbarVisibilityListener: OverviewProxyListener =
- object : OverviewProxyListener {
+ private val taskbarVisibilityListener: LauncherProxyListener =
+ object : LauncherProxyListener {
override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
taskbarVisible = visible
}
@@ -134,7 +134,7 @@ constructor(
public override fun onViewAttached() {
updateResources()
- overviewProxyService.addCallback(taskbarVisibilityListener)
+ launcherProxyService.addCallback(taskbarVisibilityListener)
mView.setInsetsChangedListener(delayedInsetSetter)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
mView.setConfigurationChangedListener { updateResources() }
@@ -142,7 +142,7 @@ constructor(
}
override fun onViewDetached() {
- overviewProxyService.removeCallback(taskbarVisibilityListener)
+ launcherProxyService.removeCallback(taskbarVisibilityListener)
mView.removeOnInsetsChangedListener()
mView.removeQSFragmentAttachedListener()
mView.setConfigurationChangedListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index d82f8e722744..fa40aa2bad24 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -293,7 +293,7 @@ constructor(
override fun onDensityOrFontScaleChanged() {
clock.setTextAppearance(R.style.TextAppearance_QS_Status)
date.setTextAppearance(R.style.TextAppearance_QS_Status)
- mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+ mShadeCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status)
loadConstraints()
header.minHeight =
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index c8f972774ab0..382fc7058bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -70,7 +70,7 @@ import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -152,7 +152,7 @@ public class NotificationLockscreenUserManagerImpl implements
private final List<UserChangedListener> mListeners = new ArrayList<>();
private final BroadcastDispatcher mBroadcastDispatcher;
private final NotificationClickNotifier mClickNotifier;
- private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy;
+ private final Lazy<LauncherProxyService> mLauncherProxyServiceLazy;
private final FeatureFlagsClassic mFeatureFlags;
private boolean mShowLockscreenNotifications;
private LockPatternUtils mLockPatternUtils;
@@ -235,8 +235,8 @@ public class NotificationLockscreenUserManagerImpl implements
if (!keyguardPrivateNotifications()) {
// Start the overview connection to the launcher service
// Connect if user hasn't connected yet
- if (mOverviewProxyServiceLazy.get().getProxy() == null) {
- mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+ if (mLauncherProxyServiceLazy.get().getProxy() == null) {
+ mLauncherProxyServiceLazy.get().startConnectionToCurrentUser();
}
}
} else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) {
@@ -318,7 +318,7 @@ public class NotificationLockscreenUserManagerImpl implements
Lazy<NotificationVisibilityProvider> visibilityProviderLazy,
Lazy<CommonNotifCollection> commonNotifCollectionLazy,
NotificationClickNotifier clickNotifier,
- Lazy<OverviewProxyService> overviewProxyServiceLazy,
+ Lazy<LauncherProxyService> launcherProxyServiceLazy,
KeyguardManager keyguardManager,
StatusBarStateController statusBarStateController,
@Main Executor mainExecutor,
@@ -343,7 +343,7 @@ public class NotificationLockscreenUserManagerImpl implements
mVisibilityProviderLazy = visibilityProviderLazy;
mCommonNotifCollectionLazy = commonNotifCollectionLazy;
mClickNotifier = clickNotifier;
- mOverviewProxyServiceLazy = overviewProxyServiceLazy;
+ mLauncherProxyServiceLazy = launcherProxyServiceLazy;
statusBarStateController.addCallback(this);
mLockPatternUtils = lockPatternUtils;
mKeyguardManager = keyguardManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index b7cad625b7b8..46456b841e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNoti
import com.android.systemui.statusbar.notification.domain.model.TopPinnedState
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -73,17 +74,24 @@ constructor(
OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(this.key)
}
val colors = this.promotedContent.toCustomColorsModel()
- val onClickListener =
+
+ val clickListener: () -> Unit = {
+ // The notification pipeline needs everything to run on the main thread, so keep
+ // this event on the main thread.
+ applicationScope.launch {
+ notifChipsInteractor.onPromotedNotificationChipTapped(this@toActivityChipModel.key)
+ }
+ }
+ val onClickListenerLegacy =
View.OnClickListener {
- // The notification pipeline needs everything to run on the main thread, so keep
- // this event on the main thread.
- applicationScope.launch {
- notifChipsInteractor.onPromotedNotificationChipTapped(
- this@toActivityChipModel.key
- )
- }
+ StatusBarChipsModernization.assertInLegacyMode()
+ clickListener.invoke()
}
- val clickBehavior = OngoingActivityChipModel.ClickBehavior.None
+ val clickBehavior =
+ OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification({
+ StatusBarChipsModernization.assertInNewMode()
+ clickListener.invoke()
+ })
val isShowingHeadsUpFromChipTap =
headsUpState is TopPinnedState.Pinned &&
@@ -95,7 +103,7 @@ constructor(
return OngoingActivityChipModel.Shown.IconOnly(
icon,
colors,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -105,7 +113,7 @@ constructor(
icon,
colors,
this.promotedContent.shortCriticalText,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -121,7 +129,7 @@ constructor(
return OngoingActivityChipModel.Shown.IconOnly(
icon,
colors,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -130,7 +138,7 @@ constructor(
return OngoingActivityChipModel.Shown.IconOnly(
icon,
colors,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -140,7 +148,7 @@ constructor(
icon,
colors,
time = this.promotedContent.time.time,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -149,7 +157,7 @@ constructor(
icon,
colors,
startTimeMs = this.promotedContent.time.time,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
@@ -159,7 +167,7 @@ constructor(
icon,
colors,
startTimeMs = this.promotedContent.time.time,
- onClickListener,
+ onClickListenerLegacy,
clickBehavior,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index a682f9674e2e..279792ef7536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -66,6 +66,9 @@ fun OngoingActivityChip(model: OngoingActivityChipModel.Shown, modifier: Modifie
ChipBody(model, onClick = { clickBehavior.onClick(expandable) })
}
}
+ is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> {
+ ChipBody(model, onClick = { clickBehavior.onClick() })
+ }
is OngoingActivityChipModel.ClickBehavior.None -> {
ChipBody(model, modifier = modifier)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 68c8f8cb4254..c6d6da2ad9aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -170,5 +170,8 @@ sealed class OngoingActivityChipModel {
/** The chip expands into a dialog or activity on click. */
data class ExpandAction(val onClick: (Expandable) -> Unit) : ClickBehavior
+
+ /** Clicking the chip will show the heads up notification associated with the chip. */
+ data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 3825c098ca5d..b6ef95893036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification
import android.app.Notification
+import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
import android.content.Context
import android.content.pm.LauncherApps
import android.graphics.drawable.AnimatedImageDrawable
@@ -66,6 +67,12 @@ constructor(
messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label }
}
+ if (NmSummarizationUiFlag.isEnabled) {
+ entry.sbn.notification.extras.putCharSequence(
+ EXTRA_SUMMARIZED_CONTENT, entry.ranking.summarization
+ )
+ }
+
messagingStyle.unreadMessageCount =
conversationNotificationManager.getUnreadCount(entry, recoveredBuilder)
return messagingStyle
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt
new file mode 100644
index 000000000000..feac0a514828
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification
+
+import android.app.Flags;
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/**
+ * Helper for android.app.nm_summarization and android.nm_summarization_ui. The new functionality
+ * should be enabled if either flag is enabled.
+ */
+@Suppress("NOTHING_TO_INLINE")
+object NmSummarizationUiFlag {
+ const val FLAG_DESC = "android.app.nm_summarization(_ui)"
+
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.nmSummarizationUi() || Flags.nmSummarization()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_DESC)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() =
+ RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_DESC)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index 331ef1c01596..aa5008b8416e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -40,6 +40,7 @@ internal constructor(
@RedactionType val redactionType: Int,
val isChildInGroup: Boolean,
val isGroupSummary: Boolean,
+ val summarization: String?,
) {
companion object {
@JvmStatic
@@ -61,6 +62,7 @@ internal constructor(
AsyncGroupHeaderViewInflation.isEnabled &&
!oldAdjustment.isGroupSummary &&
newAdjustment.isGroupSummary -> true
+ oldAdjustment.summarization != newAdjustment.summarization -> true
else -> false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 97e55c19d2f4..465bc288cbc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -152,5 +152,6 @@ constructor(
},
isChildInGroup = entry.hasEverBeenGroupChild(),
isGroupSummary = entry.hasEverBeenGroupSummary(),
+ summarization = entry.ranking.summarization
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 1ff0d9262476..92c10abff735 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -288,14 +288,21 @@ public class HybridConversationNotificationView extends HybridNotificationView {
public void setText(
CharSequence titleText,
CharSequence contentText,
- CharSequence conversationSenderName
+ CharSequence conversationSenderName,
+ @Nullable String summarization
) {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
- if (conversationSenderName == null) {
+ if (summarization != null) {
mConversationSenderName.setVisibility(GONE);
+ titleText = null;
+ contentText = summarization;
} else {
- mConversationSenderName.setVisibility(VISIBLE);
- mConversationSenderName.setText(conversationSenderName);
+ if (conversationSenderName == null) {
+ mConversationSenderName.setVisibility(GONE);
+ } else {
+ mConversationSenderName.setVisibility(VISIBLE);
+ mConversationSenderName.setText(conversationSenderName);
+ }
}
// TODO (b/217799515): super.bind() doesn't use contentView, remove the contentView
// argument when the flag is removed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index c0dbb37c1b36..13ed6c449797 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -217,7 +217,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
messagingStyle,
builder,
row.getContext(),
- false
+ false,
+ entry.getRanking().getSummarization()
);
// If the messagingStyle is null, we want to inflate the normal view
isConversation = viewModel.isConversation();
@@ -239,7 +240,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
messagingStyle,
builder,
row.getContext(),
- true);
+ true,
+ entry.getRanking().getSummarization());
} else {
result.mPublicInflatedSingleLineViewModel =
SingleLineViewInflater.inflateRedactedSingleLineViewModel(
@@ -1318,7 +1320,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
messagingStyle,
recoveredBuilder,
mContext,
- false
+ false,
+ mEntry.getRanking().getSummarization()
);
result.mInflatedSingleLineView =
SingleLineViewInflater.inflatePrivateSingleLineView(
@@ -1338,7 +1341,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
messagingStyle,
recoveredBuilder,
mContext,
- true
+ true,
+ null
);
} else {
result.mPublicInflatedSingleLineViewModel =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 0b299d965b09..f4aae6e288a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -719,6 +719,7 @@ constructor(
builder = builder,
systemUiContext = systemUiContext,
redactText = false,
+ summarization = entry.ranking.summarization
)
} else null
@@ -735,6 +736,7 @@ constructor(
builder = builder,
systemUiContext = systemUiContext,
redactText = true,
+ summarization = null
)
} else {
SingleLineViewInflater.inflateRedactedSingleLineViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index fe2803bfc5d6..c051513ef3b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -61,6 +61,7 @@ internal object SingleLineViewInflater {
builder: Notification.Builder,
systemUiContext: Context,
redactText: Boolean,
+ summarization: String?
): SingleLineViewModel {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) {
return SingleLineViewModel(null, null, null)
@@ -108,6 +109,7 @@ internal object SingleLineViewInflater {
conversationSenderName =
if (isGroupConversation) conversationTextData?.senderName else null,
avatar = conversationAvatar,
+ summarization = summarization
)
return SingleLineViewModel(
@@ -132,6 +134,7 @@ internal object SingleLineViewInflater {
.ic_redacted_notification_single_line_icon
)
),
+ null
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
index a17197c1f8ea..a50fc4c7986a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt
@@ -32,6 +32,7 @@ object SingleLineViewBinder {
viewModel?.titleText,
viewModel?.contentText,
viewModel?.conversationData?.conversationSenderName,
+ viewModel?.conversationData?.summarization
)
} else {
// bind the title and content text views
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
index d583fa5d97ed..32ded25f18a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt
@@ -46,6 +46,7 @@ data class SingleLineViewModel(
data class ConversationData(
val conversationSenderName: CharSequence?,
val avatar: ConversationAvatar,
+ val summarization: String?
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 69b7e892a380..9795cda97f37 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -49,8 +49,10 @@ import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
@@ -80,6 +82,7 @@ fun TutorialSelectionScreen(
}
),
) {
+ val padding = if (hasCompactWindowSize()) 24.dp else 60.dp
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
@@ -88,7 +91,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -98,7 +101,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -106,7 +109,7 @@ fun TutorialSelectionScreen(
// because other composables have weight 1, Done button will be positioned first
DoneButton(
onDoneButtonClicked = onDoneButtonClicked,
- modifier = Modifier.padding(horizontal = 60.dp),
+ modifier = Modifier.padding(horizontal = padding),
)
}
}
@@ -146,7 +149,7 @@ private fun VerticalSelectionButtons(
lastSelectedScreen: Screen,
) {
Column(
- verticalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
@@ -244,8 +247,13 @@ private fun TutorialButton(
modifier = Modifier.width(30.dp).height(30.dp),
tint = iconColor,
)
- Spacer(modifier = Modifier.height(16.dp))
- Text(text = text, style = MaterialTheme.typography.headlineLarge, color = iconColor)
+ if (!hasCompactWindowSize()) Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.headlineLarge,
+ color = iconColor,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java b/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java
new file mode 100644
index 000000000000..8a5f7eaf8776
--- /dev/null
+++ b/packages/SystemUI/src/com/google/android/systemui/lowlightclock/LowLightClockDreamService.java
@@ -0,0 +1,161 @@
+/*
+ * 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.google.android.systemui.lowlightclock;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.service.dreams.DreamService;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+import android.widget.TextView;
+
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.lowlightclock.ChargingStatusProvider;
+import com.android.systemui.lowlightclock.LowLightClockAnimationProvider;
+import com.android.systemui.lowlightclock.LowLightDisplayController;
+import com.android.systemui.res.R;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * A dark themed text clock dream to be shown when the device is in a low light environment.
+ */
+public class LowLightClockDreamService extends DreamService implements
+ LowLightTransitionCoordinator.LowLightExitListener {
+ private static final String TAG = "LowLightClockDreamService";
+
+ private final ChargingStatusProvider mChargingStatusProvider;
+ private final LowLightDisplayController mDisplayController;
+ private final LowLightClockAnimationProvider mAnimationProvider;
+ private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+ private boolean mIsDimBrightnessSupported = false;
+
+ private TextView mChargingStatusTextView;
+ private TextClock mTextClock;
+ @Nullable
+ private Animator mAnimationIn;
+ @Nullable
+ private Animator mAnimationOut;
+
+ @Inject
+ public LowLightClockDreamService(
+ ChargingStatusProvider chargingStatusProvider,
+ LowLightClockAnimationProvider animationProvider,
+ LowLightTransitionCoordinator lowLightTransitionCoordinator,
+ Optional<Provider<LowLightDisplayController>> displayController) {
+ super();
+
+ mAnimationProvider = animationProvider;
+ mDisplayController = displayController.map(Provider::get).orElse(null);
+ mChargingStatusProvider = chargingStatusProvider;
+ mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ setInteractive(false);
+ setFullscreen(true);
+
+ setContentView(LayoutInflater.from(getApplicationContext()).inflate(
+ R.layout.low_light_clock_dream, null));
+
+ mTextClock = findViewById(R.id.low_light_text_clock);
+
+ mChargingStatusTextView = findViewById(R.id.charging_status_text_view);
+
+ mChargingStatusProvider.startUsing(this::updateChargingMessage);
+
+ mLowLightTransitionCoordinator.setLowLightExitListener(this);
+ }
+
+ @Override
+ public void onDreamingStarted() {
+ mAnimationIn = mAnimationProvider.provideAnimationIn(mTextClock, mChargingStatusTextView);
+ mAnimationIn.start();
+
+ if (mDisplayController != null) {
+ mIsDimBrightnessSupported = mDisplayController.isDisplayBrightnessModeSupported();
+
+ if (mIsDimBrightnessSupported) {
+ Log.v(TAG, "setting dim brightness state");
+ mDisplayController.setDisplayBrightnessModeEnabled(true);
+ } else {
+ Log.v(TAG, "dim brightness not supported");
+ }
+ }
+ }
+
+ @Override
+ public void onDreamingStopped() {
+ if (mIsDimBrightnessSupported) {
+ Log.v(TAG, "clearing dim brightness state");
+ mDisplayController.setDisplayBrightnessModeEnabled(false);
+ }
+ }
+
+ @Override
+ public void onWakeUp() {
+ if (mAnimationIn != null) {
+ mAnimationIn.cancel();
+ }
+ mAnimationOut = mAnimationProvider.provideAnimationOut(mTextClock, mChargingStatusTextView);
+ mAnimationOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ LowLightClockDreamService.super.onWakeUp();
+ }
+ });
+ mAnimationOut.start();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mAnimationOut != null) {
+ mAnimationOut.cancel();
+ }
+
+ mChargingStatusProvider.stopUsing();
+
+ mLowLightTransitionCoordinator.setLowLightExitListener(null);
+ }
+
+ private void updateChargingMessage(boolean showChargingStatus, String chargingStatusMessage) {
+ mChargingStatusTextView.setText(chargingStatusMessage);
+ mChargingStatusTextView.setVisibility(showChargingStatus ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
+ public Animator onBeforeExitLowLight() {
+ mAnimationOut = mAnimationProvider.provideAnimationOut(mTextClock, mChargingStatusTextView);
+ mAnimationOut.start();
+
+ // Return the animator so that the transition coordinator waits for the low light exit
+ // animations to finish before entering low light, as otherwise the default DreamActivity
+ // animation plays immediately and there's no time for this animation to play.
+ return mAnimationOut;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 057ddcd54e68..8bfd2545ff2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -21,7 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_
import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
import static com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize;
-import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.recents.LauncherProxyService.LauncherProxyListener;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
import static org.mockito.ArgumentMatchers.any;
@@ -53,7 +53,7 @@ import androidx.test.filters.SmallTest;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
@@ -80,13 +80,13 @@ public class MagnificationTest extends SysuiTestCase {
@Mock
private IMagnificationConnectionCallback mConnectionCallback;
@Mock
- private OverviewProxyService mOverviewProxyService;
+ private LauncherProxyService mLauncherProxyService;
@Mock
private SecureSettings mSecureSettings;
private CommandQueue mCommandQueue;
private MagnificationImpl mMagnification;
- private OverviewProxyListener mOverviewProxyListener;
+ private LauncherProxyListener mLauncherProxyListener;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@Mock
@@ -130,7 +130,7 @@ public class MagnificationTest extends SysuiTestCase {
mMagnification = new MagnificationImpl(getContext(),
getContext().getMainThreadHandler(), mContext.getMainExecutor(),
mCommandQueue, mModeSwitchesController,
- mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
+ mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker,
getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
getContext().getSystemService(AccessibilityManager.class),
mViewCaptureAwareWindowManager);
@@ -140,10 +140,10 @@ public class MagnificationTest extends SysuiTestCase {
mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController);
mMagnification.start();
- final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(OverviewProxyListener.class);
- verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture());
- mOverviewProxyListener = listenerArgumentCaptor.getValue();
+ final ArgumentCaptor<LauncherProxyListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(LauncherProxyListener.class);
+ verify(mLauncherProxyService).addCallback(listenerArgumentCaptor.capture());
+ mLauncherProxyListener = listenerArgumentCaptor.getValue();
}
@Test
@@ -336,7 +336,7 @@ public class MagnificationTest extends SysuiTestCase {
@Test
public void overviewProxyIsConnected_noController_resetFlag() {
- mOverviewProxyListener.onConnectionChanged(true);
+ mLauncherProxyListener.onConnectionChanged(true);
verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false);
verify(mSysUiState).commitUpdate(mContext.getDisplayId());
@@ -349,7 +349,7 @@ public class MagnificationTest extends SysuiTestCase {
mContext.getSystemService(DisplayManager.class), mController);
mMagnification.mWindowMagnificationControllerSupplier.get(TEST_DISPLAY);
- mOverviewProxyListener.onConnectionChanged(true);
+ mLauncherProxyListener.onConnectionChanged(true);
verify(mController).updateSysUIStateFlag();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
new file mode 100644
index 000000000000..43ee388e44a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/AmbientLightModeMonitorTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.lowlightclock
+
+import android.hardware.Sensor
+import android.hardware.SensorEventListener
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.sensors.AsyncSensorManager
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AmbientLightModeMonitorTest : SysuiTestCase() {
+ @Mock private lateinit var sensorManager: AsyncSensorManager
+ @Mock private lateinit var sensor: Sensor
+ @Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
+
+ private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ ambientLightModeMonitor =
+ AmbientLightModeMonitor(Optional.of(algorithm), sensorManager, Optional.of(sensor))
+ }
+
+ @Test
+ fun shouldRegisterSensorEventListenerOnStart() {
+ val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+ ambientLightModeMonitor.start(callback)
+
+ verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
+ }
+
+ @Test
+ fun shouldUnregisterSensorEventListenerOnStop() {
+ val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+ ambientLightModeMonitor.start(callback)
+
+ val sensorEventListener = captureSensorEventListener()
+
+ ambientLightModeMonitor.stop()
+
+ verify(sensorManager).unregisterListener(eq(sensorEventListener))
+ }
+
+ @Test
+ fun shouldStartDebounceAlgorithmOnStart() {
+ val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+ ambientLightModeMonitor.start(callback)
+
+ verify(algorithm).start(eq(callback))
+ }
+
+ @Test
+ fun shouldStopDebounceAlgorithmOnStop() {
+ val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+ ambientLightModeMonitor.start(callback)
+ ambientLightModeMonitor.stop()
+
+ verify(algorithm).stop()
+ }
+
+ @Test
+ fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
+ val ambientLightModeMonitor =
+ AmbientLightModeMonitor(Optional.of(algorithm), sensorManager, Optional.empty())
+
+ val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+ ambientLightModeMonitor.start(callback)
+
+ verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
+ }
+
+ // Captures [SensorEventListener], assuming it has been registered with [sensorManager].
+ private fun captureSensorEventListener(): SensorEventListener {
+ val captor = ArgumentCaptor.forClass(SensorEventListener::class.java)
+ verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
+ return captor.value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java
new file mode 100644
index 000000000000..2c8c1e1e70b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ChargingStatusProviderTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.lowlightclock;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.BatteryManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ChargingStatusProviderTest extends SysuiTestCase {
+ @Mock
+ private Resources mResources;
+ @Mock
+ private IBatteryStats mBatteryInfo;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private ChargingStatusProvider.Callback mCallback;
+
+ private ChargingStatusProvider mProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mProvider = new ChargingStatusProvider(
+ mContext, mResources, mBatteryInfo, mKeyguardUpdateMonitor);
+ }
+
+ @Test
+ public void testStartUsingReportsStatusToCallback() {
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ }
+
+ @Test
+ public void testStartUsingRegistersCallbackWithKeyguardUpdateMonitor() {
+ mProvider.startUsing(mCallback);
+ verify(mKeyguardUpdateMonitor).registerCallback(any());
+ }
+
+ @Test
+ public void testCallbackNotCalledAfterStopUsing() {
+ mProvider.startUsing(mCallback);
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ mProvider.stopUsing();
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getChargingBattery());
+ verify(mCallback, never()).onChargingStatusChanged(eq(true), any());
+ }
+
+ @Test
+ public void testKeyguardUpdateMonitorCallbackRemovedAfterStopUsing() {
+ mProvider.startUsing(mCallback);
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ mProvider.stopUsing();
+ verify(mKeyguardUpdateMonitor)
+ .removeCallback(keyguardUpdateMonitorCallbackArgumentCaptor.getValue());
+ }
+
+ @Test
+ public void testChargingStatusReportsHideWhenNotPluggedIn() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getUnpluggedBattery());
+ // Once for init() and once for the status change.
+ verify(mCallback, times(2)).onChargingStatusChanged(false, null);
+ }
+
+ @Test
+ public void testChargingStatusReportsShowWhenBatteryOverheated() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getBatteryDefender());
+ verify(mCallback).onChargingStatusChanged(eq(true), any());
+ }
+
+ @Test
+ public void testChargingStatusReportsShowWhenPluggedIn() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getChargingBattery());
+ verify(mCallback).onChargingStatusChanged(eq(true), any());
+ }
+
+ @Test
+ public void testChargingStatusReportsChargingLimitedWhenOverheated() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getBatteryDefender());
+ verify(mResources).getString(eq(R.string.keyguard_plugged_in_charging_limited), any());
+ }
+
+ @Test
+ public void testChargingStatusReportsChargedWhenCharged() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getChargedBattery());
+ verify(mResources).getString(R.string.keyguard_charged);
+ }
+
+ @Test
+ public void testChargingStatusReportsPluggedInWhenDockedAndChargingTimeUnknown() throws
+ RemoteException {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ when(mBatteryInfo.computeChargeTimeRemaining()).thenReturn(-1L);
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getChargingBattery());
+ verify(mResources).getString(
+ eq(R.string.keyguard_plugged_in_dock), any());
+ }
+
+ @Test
+ public void testChargingStatusReportsTimeRemainingWhenDockedAndCharging() throws
+ RemoteException {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ mProvider.startUsing(mCallback);
+ verify(mCallback).onChargingStatusChanged(false, null);
+ verify(mKeyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+ when(mBatteryInfo.computeChargeTimeRemaining()).thenReturn(1L);
+ keyguardUpdateMonitorCallbackArgumentCaptor.getValue()
+ .onRefreshBatteryInfo(getChargingBattery());
+ verify(mResources).getString(
+ eq(R.string.keyguard_indication_charging_time_dock), any(), any());
+ }
+
+ private BatteryStatus getUnpluggedBattery() {
+ return new BatteryStatus(BatteryManager.BATTERY_STATUS_NOT_CHARGING,
+ 80, BatteryManager.BATTERY_PLUGGED_ANY, BatteryManager.BATTERY_HEALTH_GOOD,
+ 0, true);
+ }
+
+ private BatteryStatus getChargingBattery() {
+ return new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+ 80, BatteryManager.BATTERY_PLUGGED_DOCK,
+ BatteryManager.BATTERY_HEALTH_GOOD, 0, true);
+ }
+
+ private BatteryStatus getChargedBattery() {
+ return new BatteryStatus(BatteryManager.BATTERY_STATUS_FULL,
+ 100, BatteryManager.BATTERY_PLUGGED_DOCK,
+ BatteryManager.BATTERY_HEALTH_GOOD, 0, true);
+ }
+
+ private BatteryStatus getBatteryDefender() {
+ return new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+ 80, BatteryManager.BATTERY_PLUGGED_DOCK,
+ BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE, 0, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt
new file mode 100644
index 000000000000..173f243cb2b0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/DirectBootConditionTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.lowlightclock
+
+import android.content.Intent
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class DirectBootConditionTest : SysuiTestCase() {
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var callback: Condition.Callback
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun receiverRegisteredOnStart() = runTest {
+ val condition = buildCondition(this)
+ // No receivers are registered yet
+ assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(0)
+ condition.addCallback(callback)
+ advanceUntilIdle()
+ // Receiver is registered after a callback is added
+ assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(1)
+ condition.removeCallback(callback)
+ }
+
+ @Test
+ fun unregisterReceiverOnStop() = runTest {
+ val condition = buildCondition(this)
+
+ condition.addCallback(callback)
+ advanceUntilIdle()
+
+ assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(1)
+
+ condition.removeCallback(callback)
+ advanceUntilIdle()
+
+ // Receiver is unregistered when nothing is listening to the condition
+ assertThat(fakeBroadcastDispatcher.numReceiversRegistered).isEqualTo(0)
+ }
+
+ @Test
+ fun callbackTriggeredWhenUserUnlocked() = runTest {
+ val condition = buildCondition(this)
+
+ setUserUnlocked(false)
+ condition.addCallback(callback)
+ advanceUntilIdle()
+
+ assertThat(condition.isConditionMet).isTrue()
+
+ setUserUnlocked(true)
+ advanceUntilIdle()
+
+ assertThat(condition.isConditionMet).isFalse()
+ condition.removeCallback(callback)
+ }
+
+ private fun buildCondition(scope: CoroutineScope): DirectBootCondition {
+ return DirectBootCondition(fakeBroadcastDispatcher, userManager, scope)
+ }
+
+ private fun setUserUnlocked(unlocked: Boolean) {
+ whenever(userManager.isUserUnlocked).thenReturn(unlocked)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_UNLOCKED),
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
new file mode 100644
index 000000000000..7297e0f3bff5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.statusbar.commandline.Command;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
+
+import kotlin.jvm.functions.Function0;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ForceLowLightConditionTest extends SysuiTestCase {
+ @Mock
+ private CommandRegistry mCommandRegistry;
+
+ @Mock
+ private Condition.Callback mCallback;
+
+ @Mock
+ private PrintWriter mPrintWriter;
+
+ @Mock
+ CoroutineScope mScope;
+
+ private ForceLowLightCondition mCondition;
+ private Command mCommand;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCondition = new ForceLowLightCondition(mScope, mCommandRegistry);
+ mCondition.addCallback(mCallback);
+ ArgumentCaptor<Function0<Command>> commandCaptor =
+ ArgumentCaptor.forClass(Function0.class);
+ verify(mCommandRegistry).registerCommand(eq(ForceLowLightCondition.COMMAND_ROOT),
+ commandCaptor.capture());
+ mCommand = commandCaptor.getValue().invoke();
+ }
+
+ @Test
+ public void testEnableLowLight() {
+ mCommand.execute(mPrintWriter,
+ Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
+ verify(mCallback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionSet()).isTrue();
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ @Test
+ public void testDisableLowLight() {
+ mCommand.execute(mPrintWriter,
+ Arrays.asList(ForceLowLightCondition.COMMAND_DISABLE_LOW_LIGHT));
+ verify(mCallback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionSet()).isTrue();
+ assertThat(mCondition.isConditionMet()).isFalse();
+ }
+
+ @Test
+ public void testClearEnableLowLight() {
+ mCommand.execute(mPrintWriter,
+ Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
+ verify(mCallback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionSet()).isTrue();
+ assertThat(mCondition.isConditionMet()).isTrue();
+ Mockito.clearInvocations(mCallback);
+ mCommand.execute(mPrintWriter,
+ Arrays.asList(ForceLowLightCondition.COMMAND_CLEAR_LOW_LIGHT));
+ verify(mCallback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionSet()).isFalse();
+ assertThat(mCondition.isConditionMet()).isFalse();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt
new file mode 100644
index 000000000000..663880f098cd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockAnimationProviderTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.lowlightclock
+
+import android.animation.Animator
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class LowLightClockAnimationProviderTest : SysuiTestCase() {
+
+ private val underTest by lazy {
+ LowLightClockAnimationProvider(
+ Y_TRANSLATION_ANIMATION_OFFSET,
+ Y_TRANSLATION_ANIMATION_DURATION_MILLIS,
+ ALPHA_ANIMATION_IN_START_DELAY_MILLIS,
+ ALPHA_ANIMATION_DURATION_MILLIS,
+ )
+ }
+
+ @Test
+ fun animationOutEndsImmediatelyIfViewIsNull() {
+ val animator = underTest.provideAnimationOut(null, null)
+
+ val listener = mock<Animator.AnimatorListener>()
+ animator.addListener(listener)
+
+ animator.start()
+ verify(listener).onAnimationStart(any(), eq(false))
+ verify(listener).onAnimationEnd(any(), eq(false))
+ }
+
+ @Test
+ fun animationInEndsImmediatelyIfViewIsNull() {
+ val animator = underTest.provideAnimationIn(null, null)
+
+ val listener = mock<Animator.AnimatorListener>()
+ animator.addListener(listener)
+
+ animator.start()
+ verify(listener).onAnimationStart(any(), eq(false))
+ verify(listener).onAnimationEnd(any(), eq(false))
+ }
+
+ private companion object {
+ const val Y_TRANSLATION_ANIMATION_OFFSET = 100
+ const val Y_TRANSLATION_ANIMATION_DURATION_MILLIS = 100L
+ const val ALPHA_ANIMATION_IN_START_DELAY_MILLIS = 200L
+ const val ALPHA_ANIMATION_DURATION_MILLIS = 300L
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java
new file mode 100644
index 000000000000..22a13cc41425
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightClockDreamServiceTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.lowlightclock;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.Animator;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.SysuiTestCase;
+
+import com.google.android.systemui.lowlightclock.LowLightClockDreamService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightClockDreamServiceTest extends SysuiTestCase {
+ @Mock
+ private ChargingStatusProvider mChargingStatusProvider;
+ @Mock
+ private LowLightDisplayController mDisplayController;
+ @Mock
+ private LowLightClockAnimationProvider mAnimationProvider;
+ @Mock
+ private LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+ @Mock
+ Animator mAnimationInAnimator;
+ @Mock
+ Animator mAnimationOutAnimator;
+
+ private LowLightClockDreamService mService;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mService = new LowLightClockDreamService(
+ mChargingStatusProvider,
+ mAnimationProvider,
+ mLowLightTransitionCoordinator,
+ Optional.of(() -> mDisplayController));
+
+ when(mAnimationProvider.provideAnimationIn(any(), any())).thenReturn(mAnimationInAnimator);
+ when(mAnimationProvider.provideAnimationOut(any())).thenReturn(
+ mAnimationOutAnimator);
+ }
+
+ @Test
+ public void testSetDbmStateWhenSupported() throws RemoteException {
+ when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(true);
+
+ mService.onDreamingStarted();
+
+ verify(mDisplayController).setDisplayBrightnessModeEnabled(true);
+ }
+
+ @Test
+ public void testNotSetDbmStateWhenNotSupported() throws RemoteException {
+ when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(false);
+
+ mService.onDreamingStarted();
+
+ verify(mDisplayController, never()).setDisplayBrightnessModeEnabled(anyBoolean());
+ }
+
+ @Test
+ public void testClearDbmState() throws RemoteException {
+ when(mDisplayController.isDisplayBrightnessModeSupported()).thenReturn(true);
+
+ mService.onDreamingStarted();
+ clearInvocations(mDisplayController);
+
+ mService.onDreamingStopped();
+
+ verify(mDisplayController).setDisplayBrightnessModeEnabled(false);
+ }
+
+ @Test
+ public void testAnimationsStartedOnDreamingStarted() {
+ mService.onDreamingStarted();
+
+ // Entry animation started.
+ verify(mAnimationInAnimator).start();
+ }
+
+ @Test
+ public void testAnimationsStartedOnWakeUp() {
+ // Start dreaming then wake up.
+ mService.onDreamingStarted();
+ mService.onWakeUp();
+
+ // Entry animation started.
+ verify(mAnimationInAnimator).cancel();
+
+ // Exit animation started.
+ verify(mAnimationOutAnimator).start();
+ }
+
+ @Test
+ public void testAnimationsStartedBeforeExitingLowLight() {
+ mService.onBeforeExitLowLight();
+
+ // Exit animation started.
+ verify(mAnimationOutAnimator).start();
+ }
+
+ @Test
+ public void testWakeUpAnimationCancelledOnDetach() {
+ mService.onWakeUp();
+
+ // Exit animation started.
+ verify(mAnimationOutAnimator).start();
+
+ mService.onDetachedFromWindow();
+
+ verify(mAnimationOutAnimator).cancel();
+ }
+
+ @Test
+ public void testExitLowLightAnimationCancelledOnDetach() {
+ mService.onBeforeExitLowLight();
+
+ // Exit animation started.
+ verify(mAnimationOutAnimator).start();
+
+ mService.onDetachedFromWindow();
+
+ verify(mAnimationOutAnimator).cancel();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
new file mode 100644
index 000000000000..2c216244985e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.SysuiTestCase;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightConditionTest extends SysuiTestCase {
+ @Mock
+ private AmbientLightModeMonitor mAmbientLightModeMonitor;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ CoroutineScope mScope;
+ private LowLightCondition mCondition;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mCondition = new LowLightCondition(mScope, mAmbientLightModeMonitor, mUiEventLogger);
+ mCondition.start();
+ }
+
+ @Test
+ public void testLowLightFalse() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+ assertThat(mCondition.isConditionMet()).isFalse();
+ }
+
+ @Test
+ public void testLowLightTrue() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ @Test
+ public void testUndecidedLowLightStateIgnored() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ assertThat(mCondition.isConditionMet()).isTrue();
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED);
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ @Test
+ public void testLowLightChange() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+ assertThat(mCondition.isConditionMet()).isFalse();
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ @Test
+ public void testResetIsConditionMetUponStop() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ assertThat(mCondition.isConditionMet()).isTrue();
+
+ mCondition.stop();
+ assertThat(mCondition.isConditionMet()).isFalse();
+ }
+
+ @Test
+ public void testLoggingAmbientLightNotLowToLow() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ // Only logged once.
+ verify(mUiEventLogger, times(1)).log(any());
+ // Logged with the correct state.
+ verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_DARK);
+ }
+
+ @Test
+ public void testLoggingAmbientLightLowToLow() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ reset(mUiEventLogger);
+
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ // Doesn't log.
+ verify(mUiEventLogger, never()).log(any());
+ }
+
+ @Test
+ public void testLoggingAmbientLightNotLowToNotLow() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+ // Doesn't log.
+ verify(mUiEventLogger, never()).log(any());
+ }
+
+ @Test
+ public void testLoggingAmbientLightLowToNotLow() {
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ reset(mUiEventLogger);
+
+ changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
+ // Only logged once.
+ verify(mUiEventLogger).log(any());
+ // Logged with the correct state.
+ verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
+ }
+
+ private void changeLowLightMode(int mode) {
+ ArgumentCaptor<AmbientLightModeMonitor.Callback> ambientLightCallbackCaptor =
+ ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
+ verify(mAmbientLightModeMonitor).start(ambientLightCallbackCaptor.capture());
+ ambientLightCallbackCaptor.getValue().onChange(mode);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java
new file mode 100644
index 000000000000..69485e848a6a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightMonitorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.lowlightclock;
+
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT;
+import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_REGULAR;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.dream.lowlight.LowLightDreamManager;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import dagger.Lazy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightMonitorTest extends SysuiTestCase {
+
+ @Mock
+ private Lazy<LowLightDreamManager> mLowLightDreamManagerLazy;
+ @Mock
+ private LowLightDreamManager mLowLightDreamManager;
+ @Mock
+ private Monitor mMonitor;
+ @Mock
+ private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private LowLightLogger mLogger;
+
+ private LowLightMonitor mLowLightMonitor;
+
+ @Mock
+ Lazy<Set<Condition>> mLazyConditions;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Mock
+ private ComponentName mDreamComponent;
+
+ Condition mCondition = mock(Condition.class);
+ Set<Condition> mConditionSet = Set.of(mCondition);
+
+ @Captor
+ ArgumentCaptor<Monitor.Subscription> mPreconditionsSubscriptionCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mLowLightDreamManagerLazy.get()).thenReturn(mLowLightDreamManager);
+ when(mLazyConditions.get()).thenReturn(mConditionSet);
+ mLowLightMonitor = new LowLightMonitor(mLowLightDreamManagerLazy,
+ mMonitor, mLazyConditions, mScreenLifecycle, mLogger, mDreamComponent,
+ mPackageManager);
+ }
+
+ @Test
+ public void testSetAmbientLowLightWhenInLowLight() {
+ mLowLightMonitor.onConditionsChanged(true);
+ // Verify setting low light when condition is true
+ verify(mLowLightDreamManager).setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+ }
+
+ @Test
+ public void testExitAmbientLowLightWhenNotInLowLight() {
+ mLowLightMonitor.onConditionsChanged(true);
+ mLowLightMonitor.onConditionsChanged(false);
+ // Verify ambient light toggles back to light mode regular
+ verify(mLowLightDreamManager).setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
+ }
+
+ @Test
+ public void testStartMonitorLowLightConditionsWhenScreenTurnsOn() {
+ mLowLightMonitor.onScreenTurnedOn();
+
+ // Verify subscribing to low light conditions monitor when screen turns on.
+ verify(mMonitor).addSubscription(any());
+ }
+
+ @Test
+ public void testStopMonitorLowLightConditionsWhenScreenTurnsOff() {
+ final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+ when(mMonitor.addSubscription(any())).thenReturn(token);
+ mLowLightMonitor.onScreenTurnedOn();
+
+ // Verify removing subscription when screen turns off.
+ mLowLightMonitor.onScreenTurnedOff();
+ verify(mMonitor).removeSubscription(token);
+ }
+
+ @Test
+ public void testSubscribeToLowLightConditionsOnlyOnceWhenScreenTurnsOn() {
+ final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+ when(mMonitor.addSubscription(any())).thenReturn(token);
+
+ mLowLightMonitor.onScreenTurnedOn();
+ mLowLightMonitor.onScreenTurnedOn();
+ // Verify subscription is only added once.
+ verify(mMonitor, times(1)).addSubscription(any());
+ }
+
+ @Test
+ public void testSubscribedToExpectedConditions() {
+ final Monitor.Subscription.Token token = mock(Monitor.Subscription.Token.class);
+ when(mMonitor.addSubscription(any())).thenReturn(token);
+
+ mLowLightMonitor.onScreenTurnedOn();
+ mLowLightMonitor.onScreenTurnedOn();
+ Set<Condition> conditions = captureConditions();
+ // Verify Monitor is subscribed to the expected conditions
+ assertThat(conditions).isEqualTo(mConditionSet);
+ }
+
+ @Test
+ public void testNotUnsubscribeIfNotSubscribedWhenScreenTurnsOff() {
+ mLowLightMonitor.onScreenTurnedOff();
+
+ // Verify doesn't remove subscription since there is none.
+ verify(mMonitor, never()).removeSubscription(any());
+ }
+
+ @Test
+ public void testSubscribeIfScreenIsOnWhenStarting() {
+ when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
+ mLowLightMonitor.start();
+ // Verify to add subscription on start if the screen state is on
+ verify(mMonitor, times(1)).addSubscription(any());
+ }
+
+ @Test
+ public void testNoSubscribeIfDreamNotPresent() {
+ LowLightMonitor lowLightMonitor = new LowLightMonitor(mLowLightDreamManagerLazy,
+ mMonitor, mLazyConditions, mScreenLifecycle, mLogger, null, mPackageManager);
+ when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
+ lowLightMonitor.start();
+ verify(mScreenLifecycle, never()).addObserver(any());
+ }
+
+ private Set<Condition> captureConditions() {
+ verify(mMonitor).addSubscription(mPreconditionsSubscriptionCaptor.capture());
+ return mPreconditionsSubscriptionCaptor.getValue().getConditions();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
new file mode 100644
index 000000000000..366c071fb93f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.lowlightclock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
+
+import kotlinx.coroutines.CoroutineScope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScreenSaverEnabledConditionTest extends SysuiTestCase {
+ @Mock
+ private Resources mResources;
+ @Mock
+ private SecureSettings mSecureSettings;
+ @Mock
+ CoroutineScope mScope;
+ @Captor
+ private ArgumentCaptor<ContentObserver> mSettingsObserverCaptor;
+ private ScreenSaverEnabledCondition mCondition;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ // Default dreams to enabled by default
+ doReturn(true).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_dreamsEnabledByDefault);
+
+ mCondition = new ScreenSaverEnabledCondition(mScope, mResources, mSecureSettings);
+ }
+
+ @Test
+ public void testScreenSaverInitiallyEnabled() {
+ setScreenSaverEnabled(true);
+ mCondition.start();
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ @Test
+ public void testScreenSaverInitiallyDisabled() {
+ setScreenSaverEnabled(false);
+ mCondition.start();
+ assertThat(mCondition.isConditionMet()).isFalse();
+ }
+
+ @Test
+ public void testScreenSaverStateChanges() {
+ setScreenSaverEnabled(false);
+ mCondition.start();
+ assertThat(mCondition.isConditionMet()).isFalse();
+
+ setScreenSaverEnabled(true);
+ final ContentObserver observer = captureSettingsObserver();
+ observer.onChange(/* selfChange= */ false);
+ assertThat(mCondition.isConditionMet()).isTrue();
+ }
+
+ private void setScreenSaverEnabled(boolean enabled) {
+ when(mSecureSettings.getIntForUser(eq(Settings.Secure.SCREENSAVER_ENABLED), anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(enabled ? 1 : 0);
+ }
+
+ private ContentObserver captureSettingsObserver() {
+ verify(mSecureSettings).registerContentObserverForUserSync(
+ eq(Settings.Secure.SCREENSAVER_ENABLED),
+ mSettingsObserverCaptor.capture(), eq(UserHandle.USER_CURRENT));
+ return mSettingsObserverCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index d59a404b15bb..0924df2538e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -52,7 +52,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.views.NavigationBar;
-import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -109,7 +109,7 @@ public class NavigationBarControllerImplTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mNavigationBarController = spy(
new NavigationBarControllerImpl(mContext,
- mock(OverviewProxyService.class),
+ mock(LauncherProxyService.class),
mock(NavigationModeController.class),
mock(SysUiState.class),
mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
index a192446e535b..50b8f37f8d25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.dialog
import android.content.Intent
import android.os.Handler
import android.os.fakeExecutorHandler
+import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.telephonyManager
@@ -38,8 +39,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.flags.setFlagValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -62,6 +62,8 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
+@EnableSceneContainer
+@EnableFlags(Flags.FLAG_QS_TILE_DETAILED_VIEW, Flags.FLAG_DUAL_SHADE)
@UiThreadTest
class InternetDetailsContentManagerTest : SysuiTestCase() {
private val kosmos = Kosmos()
@@ -74,11 +76,8 @@ class InternetDetailsContentManagerTest : SysuiTestCase() {
private val internetDetailsContentController: InternetDetailsContentController =
mock<InternetDetailsContentController>()
private val keyguard: KeyguardStateController = mock<KeyguardStateController>()
- private val dialogTransitionAnimator: DialogTransitionAnimator =
- mock<DialogTransitionAnimator>()
private val bgExecutor = FakeExecutor(FakeSystemClock())
private lateinit var internetDetailsContentManager: InternetDetailsContentManager
- private var subTitle: View? = null
private var ethernet: LinearLayout? = null
private var mobileDataLayout: LinearLayout? = null
private var mobileToggleSwitch: Switch? = null
@@ -96,8 +95,6 @@ class InternetDetailsContentManagerTest : SysuiTestCase() {
@Before
fun setUp() {
- // TODO: b/377388104 enable this flag after integrating with details view.
- mSetFlagsRule.setFlagValue(Flags.FLAG_QS_TILE_DETAILED_VIEW, false)
whenever(telephonyManager.createForSubscriptionId(ArgumentMatchers.anyInt()))
.thenReturn(telephonyManager)
whenever(internetWifiEntry.title).thenReturn(WIFI_TITLE)
@@ -133,9 +130,7 @@ class InternetDetailsContentManagerTest : SysuiTestCase() {
canConfigWifi = true,
coroutineScope = scope,
context = mContext,
- internetDialog = null,
uiEventLogger = mock<UiEventLogger>(),
- dialogTransitionAnimator = dialogTransitionAnimator,
handler = handler,
backgroundExecutor = bgExecutor,
keyguard = keyguard,
@@ -146,7 +141,6 @@ class InternetDetailsContentManagerTest : SysuiTestCase() {
internetDetailsContentManager.connectedWifiEntry = internetWifiEntry
internetDetailsContentManager.wifiEntriesCount = wifiEntries.size
- subTitle = contentView.requireViewById(R.id.internet_dialog_subtitle)
ethernet = contentView.requireViewById(R.id.ethernet_layout)
mobileDataLayout = contentView.requireViewById(R.id.mobile_network_layout)
mobileToggleSwitch = contentView.requireViewById(R.id.mobile_toggle)
@@ -185,32 +179,6 @@ class InternetDetailsContentManagerTest : SysuiTestCase() {
}
@Test
- fun updateContent_withApmOn_internetDialogSubTitleGone() {
- whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
- internetDetailsContentManager.updateContent(true)
- bgExecutor.runAllReady()
-
- internetDetailsContentManager.internetContentData.observe(
- internetDetailsContentManager.lifecycleOwner!!
- ) {
- assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
- }
- }
-
- @Test
- fun updateContent_withApmOff_internetDialogSubTitleVisible() {
- whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
- internetDetailsContentManager.updateContent(true)
- bgExecutor.runAllReady()
-
- internetDetailsContentManager.internetContentData.observe(
- internetDetailsContentManager.lifecycleOwner!!
- ) {
- assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
- }
- }
-
- @Test
fun updateContent_apmOffAndHasEthernet_showEthernet() {
whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index 4e1ccfb07220..69b762b470b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -40,11 +40,11 @@ import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.process.ProcessWrapper
-import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
+import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.shared.recents.IOverviewProxy
+import com.android.systemui.shared.recents.ILauncherProxy
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK
import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_ASLEEP
import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_AWAKE
@@ -83,12 +83,12 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class OverviewProxyServiceTest : SysuiTestCase() {
+class LauncherProxyServiceTest : SysuiTestCase() {
@Main private val executor: Executor = MoreExecutors.directExecutor()
private val kosmos = testKosmos()
- private lateinit var subject: OverviewProxyService
+ private lateinit var subject: LauncherProxyService
@Mock private val dumpManager = DumpManager()
@Mock private val processWrapper = ProcessWrapper()
private val displayTracker = FakeDisplayTracker(mContext)
@@ -97,10 +97,10 @@ class OverviewProxyServiceTest : SysuiTestCase() {
private val wakefulnessLifecycle =
WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
- @Mock private lateinit var overviewProxy: IOverviewProxy.Stub
+ @Mock private lateinit var launcherProxy: ILauncherProxy.Stub
@Mock private lateinit var packageManager: PackageManager
- // The following mocks belong to not-yet-tested parts of OverviewProxyService.
+ // The following mocks belong to not-yet-tested parts of LauncherProxyService.
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var shellInterface: ShellInterface
@Mock private lateinit var navBarController: NavigationBarController
@@ -127,18 +127,18 @@ class OverviewProxyServiceTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
val serviceComponent = ComponentName("test_package", "service_provider")
- context.addMockService(serviceComponent, overviewProxy)
+ context.addMockService(serviceComponent, launcherProxy)
context.addMockServiceResolver(
TestableContext.MockServiceResolver {
if (it.action == ACTION_QUICKSTEP) serviceComponent else null
}
)
- whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
- .thenReturn(overviewProxy)
- whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
+ whenever(launcherProxy.queryLocalInterface(ArgumentMatchers.anyString()))
+ .thenReturn(launcherProxy)
+ whenever(launcherProxy.asBinder()).thenReturn(launcherProxy)
// packageManager.resolveServiceAsUser has to return non-null for
- // OverviewProxyService#isEnabled to become true.
+ // LauncherProxyService#isEnabled to become true.
context.setMockPackageManager(packageManager)
whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
.thenReturn(mock(ResolveInfo::class.java))
@@ -147,7 +147,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
// return isSystemUser as true by default.
`when`(processWrapper.isSystemUser).thenReturn(true)
- subject = createOverviewProxyService(context)
+ subject = createLauncherProxyService(context)
}
@After
@@ -159,11 +159,11 @@ class OverviewProxyServiceTest : SysuiTestCase() {
fun wakefulnessLifecycle_dispatchFinishedWakingUpSetsSysUIflagToAWAKE() {
// WakefulnessLifecycle is initialized to AWAKE initially, and won't emit a noop.
wakefulnessLifecycle.dispatchFinishedGoingToSleep()
- clearInvocations(overviewProxy)
+ clearInvocations(launcherProxy)
wakefulnessLifecycle.dispatchFinishedWakingUp()
- verify(overviewProxy)
+ verify(launcherProxy)
.onSystemUiStateChanged(
longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
)
@@ -173,7 +173,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() {
wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN)
- verify(overviewProxy)
+ verify(launcherProxy)
.onSystemUiStateChanged(
longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
)
@@ -183,7 +183,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() {
wakefulnessLifecycle.dispatchFinishedGoingToSleep()
- verify(overviewProxy)
+ verify(launcherProxy)
.onSystemUiStateChanged(
longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
)
@@ -195,56 +195,56 @@ class OverviewProxyServiceTest : SysuiTestCase() {
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
)
- verify(overviewProxy)
+ verify(launcherProxy)
.onSystemUiStateChanged(
longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
)
}
@Test
- fun connectToOverviewService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
+ fun connectToLauncherService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
`when`(processWrapper.isSystemUser).thenReturn(true)
`when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
+ val ops = createLauncherProxyService(spyContext)
ops.startConnectionToCurrentUser()
verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
- fun connectToOverviewService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
+ fun connectToLauncherService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
`when`(processWrapper.isSystemUser).thenReturn(false)
`when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
+ val ops = createLauncherProxyService(spyContext)
ops.startConnectionToCurrentUser()
verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
- fun connectToOverviewService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
+ fun connectToLauncherService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
`when`(processWrapper.isSystemUser).thenReturn(false)
`when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
`when`(userManager.isUserForeground()).thenReturn(false)
val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
+ val ops = createLauncherProxyService(spyContext)
ops.startConnectionToCurrentUser()
verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), anyInt(), any())
}
@Test
- fun connectToOverviewService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
+ fun connectToLauncherService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
`when`(processWrapper.isSystemUser).thenReturn(false)
`when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
`when`(userManager.isUserForeground()).thenReturn(true)
val spyContext = spy(context)
- val ops = createOverviewProxyService(spyContext)
+ val ops = createLauncherProxyService(spyContext)
ops.startConnectionToCurrentUser()
verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
- private fun createOverviewProxyService(ctx: Context): OverviewProxyService {
- return OverviewProxyService(
+ private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
+ return LauncherProxyService(
ctx,
executor,
commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index a04ca038021e..9abe9aa5e598 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -32,8 +32,8 @@ import com.android.systemui.fragments.FragmentService
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.plugins.qs.QS
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -71,7 +71,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
private val view = mock<NotificationsQuickSettingsContainer>()
private val navigationModeController = mock<NavigationModeController>()
- private val overviewProxyService = mock<OverviewProxyService>()
+ private val mLauncherProxyService = mock<LauncherProxyService>()
private val shadeHeaderController = mock<ShadeHeaderController>()
private val shadeInteractor = mock<ShadeInteractor>()
private val fragmentService = mock<FragmentService>()
@@ -81,7 +81,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
@Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
- @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+ @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<LauncherProxyListener>
@Captor lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
@Captor lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
@Captor lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
@@ -89,7 +89,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
lateinit var underTest: NotificationsQSContainerController
private lateinit var navigationModeCallback: ModeChangedListener
- private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+ private lateinit var taskbarVisibilityCallback: LauncherProxyListener
private lateinit var windowInsetsCallback: Consumer<WindowInsets>
private lateinit var fakeSystemClock: FakeSystemClock
private lateinit var delayableExecutor: FakeExecutor
@@ -110,7 +110,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
NotificationsQSContainerController(
view,
navigationModeController,
- overviewProxyService,
+ mLauncherProxyService,
shadeHeaderController,
shadeInteractor,
fragmentService,
@@ -127,7 +127,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
overrideResource(R.dimen.qs_footer_action_inset, FOOTER_ACTIONS_INSET)
whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
.thenReturn(GESTURES_NAVIGATION)
- doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+ doNothing().`when`(mLauncherProxyService).addCallback(taskbarVisibilityCaptor.capture())
doNothing().`when`(view).setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
doNothing().`when`(view).applyConstraints(constraintSetCaptor.capture())
doNothing().`when`(view).addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
@@ -402,7 +402,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
NotificationsQSContainerController(
container,
navigationModeController,
- overviewProxyService,
+ mLauncherProxyService,
shadeHeaderController,
shadeInteractor,
fragmentService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 24f8843e935d..4c12cc886e33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -32,8 +32,8 @@ import com.android.systemui.fragments.FragmentService
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.plugins.qs.QS
-import com.android.systemui.recents.OverviewProxyService
-import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.recents.LauncherProxyService
+import com.android.systemui.recents.LauncherProxyService.LauncherProxyListener
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -70,7 +70,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
private val view = mock<NotificationsQuickSettingsContainer>()
private val navigationModeController = mock<NavigationModeController>()
- private val overviewProxyService = mock<OverviewProxyService>()
+ private val mLauncherProxyService = mock<LauncherProxyService>()
private val shadeHeaderController = mock<ShadeHeaderController>()
private val shadeInteractor = mock<ShadeInteractor>()
private val fragmentService = mock<FragmentService>()
@@ -80,7 +80,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
@Captor lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
- @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+ @Captor lateinit var taskbarVisibilityCaptor: ArgumentCaptor<LauncherProxyListener>
@Captor lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
@Captor lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
@Captor lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
@@ -88,7 +88,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
lateinit var underTest: NotificationsQSContainerController
private lateinit var navigationModeCallback: ModeChangedListener
- private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+ private lateinit var taskbarVisibilityCallback: LauncherProxyListener
private lateinit var windowInsetsCallback: Consumer<WindowInsets>
private lateinit var fakeSystemClock: FakeSystemClock
private lateinit var delayableExecutor: FakeExecutor
@@ -110,7 +110,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
NotificationsQSContainerController(
view,
navigationModeController,
- overviewProxyService,
+ mLauncherProxyService,
shadeHeaderController,
shadeInteractor,
fragmentService,
@@ -127,7 +127,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
overrideResource(R.dimen.qs_footer_action_inset, FOOTER_ACTIONS_INSET)
whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
.thenReturn(GESTURES_NAVIGATION)
- doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+ doNothing().`when`(mLauncherProxyService).addCallback(taskbarVisibilityCaptor.capture())
doNothing().`when`(view).setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
doNothing().`when`(view).applyConstraints(constraintSetCaptor.capture())
doNothing().`when`(view).addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
@@ -457,7 +457,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
NotificationsQSContainerController(
container,
navigationModeController,
- overviewProxyService,
+ mLauncherProxyService,
shadeHeaderController,
shadeInteractor,
fragmentService,
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 a5cd81ff3116..e8ab76181af2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -296,7 +296,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
verify(clock).setTextAppearance(R.style.TextAppearance_QS_Status)
verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
- verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+ verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index 1eb88c5a5616..0457255fee4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -19,10 +19,12 @@ import android.app.Notification
import android.app.Person
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
+import android.view.View.GONE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE
import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePrivateSingleLineView
@@ -90,6 +92,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
builder = notificationBuilder,
systemUiContext = context,
redactText = false,
+ summarization = null
)
// WHEN: binds the viewHolder
@@ -151,6 +154,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
builder = notificationBuilder,
systemUiContext = context,
redactText = false,
+ summarization = null
)
// WHEN: binds the view
SingleLineViewBinder.bind(viewModel, view)
@@ -200,6 +204,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
builder = notificationBuilder,
systemUiContext = context,
redactText = false,
+ summarization = null
)
// WHEN: binds the view with the view model
SingleLineViewBinder.bind(viewModel, view)
@@ -211,6 +216,70 @@ class SingleLineViewBinderTest : SysuiTestCase() {
assertNull(viewModel.conversationData)
}
+ @Test
+ @EnableFlags(AsyncHybridViewInflation.FLAG_NAME, android.app.Flags.FLAG_NM_SUMMARIZATION_UI,
+ android.app.Flags.FLAG_NM_SUMMARIZATION)
+ fun bindSummarizedGroupConversationSingleLineView() {
+ // GIVEN a row with a group conversation notification
+ val user =
+ Person.Builder()
+ .setName(USER_NAME)
+ .build()
+ val style =
+ Notification.MessagingStyle(user)
+ .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user)
+ .addMessage(
+ "How about lunch?",
+ System.currentTimeMillis(),
+ Person.Builder().setName("user2").build(),
+ )
+ .setGroupConversation(true)
+ notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID)
+ val notification = notificationBuilder.build()
+ val row = helper.createRow(notification)
+ val rb = RankingBuilder(row.entry.ranking)
+ rb.setSummarization("summary!")
+ row.entry.ranking = rb.build()
+
+ val view =
+ inflatePrivateSingleLineView(
+ isConversation = true,
+ reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE,
+ entry = row.entry,
+ context = context,
+ logger = mock(),
+ )
+ as HybridConversationNotificationView
+
+ val publicView =
+ inflatePublicSingleLineView(
+ isConversation = true,
+ reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+ entry = row.entry,
+ context = context,
+ logger = mock(),
+ )
+ as HybridConversationNotificationView
+ assertNotNull(publicView)
+
+ val viewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ notification = notification,
+ messagingStyle = style,
+ builder = notificationBuilder,
+ systemUiContext = context,
+ redactText = false,
+ summarization = "summary"
+ )
+ // WHEN: binds the view
+ SingleLineViewBinder.bind(viewModel, view)
+
+ // THEN: the single-line conversation view should only include summarization content
+ assertEquals(viewModel.conversationData?.summarization, view.textView.text)
+ assertEquals("", view.conversationSenderNameView.text)
+ assertEquals(GONE, view.conversationSenderNameView.visibility)
+ }
+
private companion object {
const val CHANNEL_ID = "CHANNEL_ID"
const val CONTENT_TITLE = "A Cool New Feature"
@@ -218,5 +287,6 @@ class SingleLineViewBinderTest : SysuiTestCase() {
const val USER_NAME = "USER_NAME"
const val MESSAGE_TEXT = "MESSAGE_TEXT"
const val SHORTCUT_ID = "Shortcut"
+ const val SUMMARIZATION = "summarization"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index ef70e277832e..13724a8b44da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -272,6 +272,35 @@ class SingleLineViewInflaterTest : SysuiTestCase() {
}
}
+ @Test
+ fun createViewModelForSummarizedConversationNotification() {
+ // Given: a non-group conversation notification
+ val notificationType = OneToOneConversation()
+ val notification = getNotification(notificationType)
+
+ // When: inflate the SingleLineViewModel
+ val singleLineViewModel = notification.makeSingleLineViewModel(notificationType)
+
+ // Then: the inflated SingleLineViewModel should be as expected
+ // titleText: Notification.ConversationTitle
+ // contentText: the last message text
+ // conversationSenderName: null, because it's not a group conversation
+ // conversationData.avatar: a single icon of the last sender
+ // summarizedText: the summary text from the ranking
+ assertEquals(CONVERSATION_TITLE, singleLineViewModel.titleText)
+ assertEquals(LAST_MESSAGE, singleLineViewModel.contentText)
+ assertNull(
+ singleLineViewModel.conversationData?.conversationSenderName,
+ "Sender name should be null for one-on-one conversation"
+ )
+ assertTrue {
+ singleLineViewModel.conversationData
+ ?.avatar
+ ?.equalsTo(SingleIcon(firstSenderIcon.loadDrawable(context))) == true
+ }
+ assertEquals("summary", singleLineViewModel.conversationData?.summarization)
+ }
+
sealed class NotificationType(val largeIcon: Icon? = null)
class NonMessaging(largeIcon: Icon? = null) : NotificationType(largeIcon)
@@ -380,7 +409,8 @@ class SingleLineViewInflaterTest : SysuiTestCase() {
if (isConversation) messagingStyle else null,
builder,
context,
- false
+ false,
+ "summary"
)
}
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
index de4bbecaaf0e..42c509eeaa0b 100644
--- a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -16,16 +16,19 @@
package android.hardware.input
+import android.hardware.input.InputGestureData.Trigger
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
import android.hardware.input.InputManager.InputDeviceListener
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
import android.view.KeyEvent
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
class FakeInputManager {
@@ -49,36 +52,79 @@ class FakeInputManager {
)
private var inputDeviceListener: InputDeviceListener? = null
+ private val customInputGestures: MutableMap<Trigger, InputGestureData> = mutableMapOf()
+ var addCustomInputGestureErrorCode = CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+
+ val inputManager: InputManager = mock {
+ on { getCustomInputGestures(any()) }.then { customInputGestures.values.toList() }
+
+ on { addCustomInputGesture(any()) }
+ .then {
+ val inputGestureData = it.getArgument<InputGestureData>(0)
+ val trigger = inputGestureData.trigger
+
+ if (customInputGestures.containsKey(trigger)) {
+ addCustomInputGestureErrorCode
+ } else {
+ customInputGestures[trigger] = inputGestureData
+ CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ }
+ }
+
+ on { removeCustomInputGesture(any()) }
+ .then {
+ val inputGestureData = it.getArgument<InputGestureData>(0)
+ val trigger = inputGestureData.trigger
+
+ if (customInputGestures.containsKey(trigger)) {
+ customInputGestures.remove(trigger)
+ CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ } else {
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
+ }
+ }
+
+ on { removeAllCustomInputGestures(any()) }.then { customInputGestures.clear() }
- val inputManager =
- mock<InputManager> {
- whenever(getInputDevice(anyInt())).thenAnswer { invocation ->
+ on { getInputGesture(any()) }
+ .then {
+ val trigger = it.getArgument<Trigger>(0)
+ customInputGestures[trigger]
+ }
+
+ on { getInputDevice(anyInt()) }
+ .thenAnswer { invocation ->
val deviceId = invocation.arguments[0] as Int
return@thenAnswer devices[deviceId]
}
- whenever(inputDeviceIds).thenAnswer {
+ on { inputDeviceIds }
+ .thenAnswer {
return@thenAnswer devices.keys.toIntArray()
}
- fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
- val deviceId = invocation.arguments[0] as Int
- val device = devices[deviceId] ?: return
- devices[deviceId] = device.copy(enabled = enabled)
- }
+ fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
+ val deviceId = invocation.arguments[0] as Int
+ val device = devices[deviceId] ?: return
+ devices[deviceId] = device.copy(enabled = enabled)
+ }
- whenever(disableInputDevice(anyInt())).thenAnswer { invocation ->
- setDeviceEnabled(invocation, enabled = false)
- }
- whenever(enableInputDevice(anyInt())).thenAnswer { invocation ->
- setDeviceEnabled(invocation, enabled = true)
- }
- whenever(deviceHasKeys(any(), any())).thenAnswer { invocation ->
+ on { disableInputDevice(anyInt()) }
+ .thenAnswer { invocation -> setDeviceEnabled(invocation, enabled = false) }
+ on { enableInputDevice(anyInt()) }
+ .thenAnswer { invocation -> setDeviceEnabled(invocation, enabled = true) }
+ on { deviceHasKeys(any(), any()) }
+ .thenAnswer { invocation ->
val deviceId = invocation.arguments[0] as Int
val keyCodes = invocation.arguments[1] as IntArray
val supportedKeyCodes = supportedKeyCodesByDeviceId[deviceId]!!
return@thenAnswer keyCodes.map { supportedKeyCodes.contains(it) }.toBooleanArray()
}
- }
+ }
+
+ fun resetCustomInputGestures() {
+ customInputGestures.clear()
+ addCustomInputGestureErrorCode = CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+ }
fun addPhysicalKeyboardIfNotPresent(deviceId: Int, enabled: Boolean = true) {
if (devices.containsKey(deviceId)) {
@@ -97,7 +143,7 @@ class FakeInputManager {
vendorId: Int = 0,
productId: Int = 0,
isFullKeyboard: Boolean = true,
- enabled: Boolean = true
+ enabled: Boolean = true,
) {
check(id > 0) { "Physical keyboard ids have to be > 0" }
addKeyboard(id, vendorId, productId, isFullKeyboard, enabled)
@@ -113,7 +159,7 @@ class FakeInputManager {
vendorId: Int = 0,
productId: Int = 0,
isFullKeyboard: Boolean = true,
- enabled: Boolean = true
+ enabled: Boolean = true,
) {
val keyboardType =
if (isFullKeyboard) InputDevice.KEYBOARD_TYPE_ALPHABETIC
@@ -152,7 +198,7 @@ class FakeInputManager {
id: Int = getId(),
type: Int = keyboardType,
sources: Int = getSources(),
- enabled: Boolean = isEnabled
+ enabled: Boolean = isEnabled,
) =
InputDevice.Builder()
.setId(id)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 93e7f2e588b0..83f4e8f5aa49 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -25,7 +25,7 @@ import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.LauncherProxyService
import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.android.systemui.user.data.repository.userRepository
import org.mockito.kotlin.mock
@@ -43,12 +43,12 @@ var Kosmos.keyboardTouchpadEduInteractor by
userRepository,
),
tutorialRepository = tutorialSchedulerRepository,
- overviewProxyService = mockOverviewProxyService,
+ launcherProxyService = mockLauncherProxyService,
metricsLogger = mockEduMetricsLogger,
clock = fakeEduClock,
)
}
var Kosmos.mockEduMetricsLogger by Kosmos.Fixture { mock<ContextualEducationMetricsLogger>() }
-var Kosmos.mockOverviewProxyService by Kosmos.Fixture { mock<OverviewProxyService>() }
+var Kosmos.mockLauncherProxyService by Kosmos.Fixture { mock<LauncherProxyService>() }
var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index f4791003c828..026f8f97d2ae 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -124,8 +124,8 @@ class FakeKeyguardTransitionRepository(
/**
* Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
*
- * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
- * way using [throughTransitionState].
+ * By default, sends steps through FINISHED (STARTED, RUNNING @0.5f, RUNNING @1f, FINISHED) but
+ * can be halted part way using [throughTransitionState].
*/
suspend fun sendTransitionSteps(
from: KeyguardState,
@@ -137,6 +137,25 @@ class FakeKeyguardTransitionRepository(
}
/**
+ * Sends a STARTED step between [from] and [to], followed by two RUNNING steps at value
+ * [throughValue] / 2 and [throughValue], calling [runCurrent] after each step.
+ */
+ suspend fun sendTransitionStepsThroughRunning(
+ from: KeyguardState,
+ to: KeyguardState,
+ testScope: TestScope,
+ throughValue: Float = 1f,
+ ) {
+ sendTransitionSteps(
+ from,
+ to,
+ testScope.testScheduler,
+ TransitionState.RUNNING,
+ throughValue,
+ )
+ }
+
+ /**
* Sends the provided [step] and makes sure that all previous [TransitionState]'s are sent when
* [fillInSteps] is true. e.g. when a step FINISHED is provided, a step with STARTED and RUNNING
* is also sent.
@@ -178,14 +197,15 @@ class FakeKeyguardTransitionRepository(
/**
* Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
*
- * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
- * way using [throughTransitionState].
+ * By default, sends steps through FINISHED (STARTED, RUNNING @0.5f, RUNNING @1f, FINISHED) but
+ * can be halted part way using [throughTransitionState].
*/
suspend fun sendTransitionSteps(
from: KeyguardState,
to: KeyguardState,
testScheduler: TestCoroutineScheduler,
throughTransitionState: TransitionState = TransitionState.FINISHED,
+ throughTransitionValue: Float = 1f,
) {
val lastStep = _transitions.replayCache.lastOrNull()
if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) {
@@ -216,13 +236,14 @@ class FakeKeyguardTransitionRepository(
throughTransitionState == TransitionState.RUNNING ||
throughTransitionState == TransitionState.FINISHED
) {
+ // Send two steps to better simulate RUNNING transitions.
sendTransitionStep(
step =
TransitionStep(
transitionState = TransitionState.RUNNING,
from = from,
to = to,
- value = 0.5f,
+ value = throughTransitionValue / 2f,
)
)
testScheduler.runCurrent()
@@ -233,7 +254,7 @@ class FakeKeyguardTransitionRepository(
transitionState = TransitionState.RUNNING,
from = from,
to = to,
- value = 1f,
+ value = throughTransitionValue,
)
)
testScheduler.runCurrent()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
index 004f97d95673..c97c4e3ba302 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -25,5 +25,6 @@ val Kosmos.occludedToPrimaryBouncerTransitionViewModel by Fixture {
OccludedToPrimaryBouncerTransitionViewModel(
animationFlow = keyguardTransitionAnimationFlow,
blurConfig = blurConfig,
+ shadeDependentFlows = shadeDependentFlows,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
index 2256c10eebc9..ed5dd454a087 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModelKosmos.kt
@@ -25,5 +25,6 @@ val Kosmos.primaryBouncerToOccludedTransitionViewModel by Fixture {
PrimaryBouncerToOccludedTransitionViewModel(
animationFlow = keyguardTransitionAnimationFlow,
blurConfig = blurConfig,
+ shadeDependentFlows = shadeDependentFlows,
)
}
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
index 81c7edf4adf1..b9dcc6160d68 100644
--- a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -32,8 +32,7 @@ import com.android.tools.r8.keepanno.annotations.UsedByReflection;
// Without this annotation, this class will be treated as unused class and be removed during build
// time.
@UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public final class ConnectivityServiceInitializerB extends SystemService {
private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
private final VcnManagementService mVcnManagementService;
diff --git a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
index c9a99d729e91..8edd63dc341f 100644
--- a/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
+++ b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
@@ -165,8 +165,7 @@ import java.util.concurrent.TimeUnit;
* @hide
*/
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
@NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
index b04e25dff276..cedb2d16808f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -79,8 +79,7 @@ import java.util.Set;
*
* @hide
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class TelephonySubscriptionTracker extends BroadcastReceiver {
@NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName();
private static final boolean LOG_DBG = false; // STOPSHIP if true
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 97f86b1bff5b..0f8b2885cf4d 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -77,8 +77,7 @@ import java.util.Set;
*
* @hide
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index 300b80f942ef..da411174f95c 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -174,8 +174,7 @@ import java.util.function.Consumer;
*
* @hide
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 38fcf09145d9..bc815eb27454 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -58,8 +58,7 @@ import java.util.concurrent.Executor;
*/
// TODO(b/388919146): Implement a more generic solution to prevent concurrent modifications on
// mListeners and mRequests
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class VcnNetworkProvider extends NetworkProvider {
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index c8c645f1276d..aff7068b6c4f 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -61,8 +61,7 @@ import java.util.concurrent.TimeUnit;
*
* <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state"
*/
-// TODO(b/374174952) Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class IpSecPacketLossDetector extends NetworkMetricMonitor {
private static final String TAG = IpSecPacketLossDetector.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index 55829a5fe978..fc9c7ac8a335 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -44,8 +44,7 @@ import java.util.concurrent.Executor;
*
* <p>This class is flag gated by "network_metric_monitor"
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public abstract class NetworkMetricMonitor implements AutoCloseable {
private static final String TAG = NetworkMetricMonitor.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 705141f3f1b4..7cb3257193a5 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -52,8 +52,7 @@ import java.util.Map;
import java.util.Set;
/** @hide */
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
class NetworkPriorityClassifier {
@NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
/**
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index bc552e7e6afd..37ec0e8f40dc 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -75,8 +75,7 @@ import java.util.TreeSet;
*
* @hide
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class UnderlyingNetworkController {
@NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 776931bad73b..164b59f6f0cd 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -52,8 +52,7 @@ import java.util.concurrent.TimeUnit;
*
* @hide
*/
-// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
-@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@TargetApi(Build.VERSION_CODES.BAKLAVA)
public class UnderlyingNetworkEvaluator {
private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91775f8eed96..8e0a7785c597 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -607,7 +607,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mLock,
mContext,
new MagnificationScaleProvider(mContext),
- Executors.newSingleThreadExecutor()
+ Executors.newSingleThreadExecutor(),
+ mContext.getMainLooper()
);
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 75ec8ea88ace..486f1f449691 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -36,6 +36,8 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -53,6 +55,7 @@ import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.wm.WindowManagerInternal;
@@ -111,6 +114,20 @@ public class MagnificationController implements MagnificationConnectionManager.C
private final Executor mBackgroundExecutor;
+ private final Handler mHandler;
+ private @PanDirection int mActivePanDirection = PAN_DIRECTION_DOWN;
+ private int mActivePanDisplay = Display.INVALID_DISPLAY;
+ private boolean mRepeatKeysEnabled = true;
+
+ private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
+ private int mActiveZoomDisplay = Display.INVALID_DISPLAY;
+
+ // TODO(b/355499907): Get initial repeat interval from repeat keys settings.
+ @VisibleForTesting
+ public static final int INITIAL_KEYBOARD_REPEAT_INTERVAL_MS = 500;
+ @VisibleForTesting
+ public static final int KEYBOARD_REPEAT_INTERVAL_MS = 60;
+
@GuardedBy("mLock")
private final SparseIntArray mCurrentMagnificationModeArray = new SparseIntArray();
@GuardedBy("mLock")
@@ -287,12 +304,13 @@ public class MagnificationController implements MagnificationConnectionManager.C
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, MagnificationScaleProvider scaleProvider,
- Executor backgroundExecutor) {
+ Executor backgroundExecutor, Looper looper) {
mAms = ams;
mLock = lock;
mContext = context;
mScaleProvider = scaleProvider;
mBackgroundExecutor = backgroundExecutor;
+ mHandler = new Handler(looper);
LocalServices.getService(WindowManagerInternal.class)
.getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
@@ -303,14 +321,20 @@ public class MagnificationController implements MagnificationConnectionManager.C
mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
mBackgroundExecutor, mAms::updateAlwaysOnMagnification);
+
+ // TODO(b/355499907): Add an observer for repeat keys enabled changes,
+ // rather than initializing once at startup.
+ mRepeatKeysEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_ENABLED, 1,
+ UserHandle.USER_CURRENT) != 0;
}
@VisibleForTesting
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, FullScreenMagnificationController fullScreenMagnificationController,
MagnificationConnectionManager magnificationConnectionManager,
- MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
- this(ams, lock, context, scaleProvider, backgroundExecutor);
+ MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper) {
+ this(ams, lock, context, scaleProvider, backgroundExecutor, looper);
mFullScreenMagnificationController = fullScreenMagnificationController;
mMagnificationConnectionManager = magnificationConnectionManager;
}
@@ -354,27 +378,60 @@ public class MagnificationController implements MagnificationConnectionManager.C
// pan diagonally) by decreasing diagonal movement by sqrt(2) to make it appear the same
// speed as non-diagonal movement.
panMagnificationByStep(displayId, direction);
+ mActivePanDirection = direction;
+ mActivePanDisplay = displayId;
+ if (mRepeatKeysEnabled) {
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
+ INITIAL_KEYBOARD_REPEAT_INTERVAL_MS);
+ }
}
@Override
public void onPanMagnificationStop(int displayId,
@MagnificationController.PanDirection int direction) {
- // TODO(b/388847283): Handle held key gestures, which can be used
- // for continuous scaling and panning, until they are released.
-
+ if (direction == mActivePanDirection) {
+ mActivePanDisplay = Display.INVALID_DISPLAY;
+ }
}
@Override
public void onScaleMagnificationStart(int displayId,
@MagnificationController.ZoomDirection int direction) {
scaleMagnificationByStep(displayId, direction);
+ mActiveZoomDirection = direction;
+ mActiveZoomDisplay = displayId;
+ if (mRepeatKeysEnabled) {
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
+ INITIAL_KEYBOARD_REPEAT_INTERVAL_MS);
+ }
}
@Override
public void onScaleMagnificationStop(int displayId,
@MagnificationController.ZoomDirection int direction) {
- // TODO(b/388847283): Handle held key gestures, which can be used
- // for continuous scaling and panning, until they are released.
+ if (direction == mActiveZoomDirection) {
+ mActiveZoomDisplay = Display.INVALID_DISPLAY;
+ }
+ }
+
+ private void maybeContinuePan() {
+ if (mActivePanDisplay != Display.INVALID_DISPLAY) {
+ panMagnificationByStep(mActivePanDisplay, mActivePanDirection);
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
+ KEYBOARD_REPEAT_INTERVAL_MS);
+ }
+ }
+
+ private void maybeContinueZoom() {
+ if (mActiveZoomDisplay != Display.INVALID_DISPLAY) {
+ scaleMagnificationByStep(mActiveZoomDisplay, mActiveZoomDirection);
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
+ KEYBOARD_REPEAT_INTERVAL_MS);
+ }
}
private void handleUserInteractionChanged(int displayId, int mode) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 25494fce9fbf..c68e54956c99 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,7 +19,6 @@ package com.android.server.autofill;
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
import static android.service.autofill.Flags.fixGetAutofillComponent;
-import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -71,7 +70,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
-import android.view.InsetsController;
import android.view.autofill.AutofillFeatureFlags;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -98,7 +96,6 @@ import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.infra.SecureSettingsServiceNameResolver;
-import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -246,8 +243,6 @@ public final class AutofillManagerService
private static final boolean DEFAULT_PCC_USE_FALLBACK = true;
- private static final boolean DBG = false;
-
public AutofillManagerService(Context context) {
super(context,
new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -306,79 +301,8 @@ public final class AutofillManagerService
mCredentialAutofillService = null;
Slog.w(TAG, "Invalid CredentialAutofillService");
}
-
- if (improveFillDialogAconfig()) {
- WindowManagerInternal windowManagerInternal = LocalServices.getService(
- WindowManagerInternal.class);
- WindowManagerInternal.ImeInsetsAnimationChangeListener
- imeInsetsAnimationChangeListener =
- new WindowManagerInternal.ImeInsetsAnimationChangeListener() {
- @Override
- public void onAnimationStart(
- @InsetsController.AnimationType int animationType, int userId) {
- if (DBG) {
- Slog.e(TAG,
- "onAnimationStart() notifyImeAnimationStart() "
- + "animationType:"
- + String.valueOf(animationType));
- }
- synchronized (mLock) {
-
- // We are mostly interested in animations that show up the IME
- if (animationType == InsetsController.ANIMATION_TYPE_HIDE) {
- // IME is going away
- mIsImeShowing = false;
- }
- if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
- return;
- }
- mIsImeShowing = true;
- mImeAnimatingWhileShowingUp = true;
- final AutofillManagerServiceImpl service =
- peekServiceForUserWithLocalBinderIdentityLocked(userId);
- if (service != null) {
- service.notifyImeAnimationStart();
- } else if (sVerbose) {
- Slog.v(TAG,
- "notifyImeAnimationStart(): no service for " + userId);
- }
- }
- }
-
- @Override
- public void onAnimationEnd(
- @InsetsController.AnimationType int animationType, int userId) {
- if (DBG) {
- Slog.e(TAG,
- "onAnimationEnd() notifyImeAnimationEnd() "
- + "animationType:"
- + String.valueOf(animationType));
- }
- // We are only interested in animations that show up the IME
- if (animationType != InsetsController.ANIMATION_TYPE_SHOW) {
- return;
- }
- mImeAnimatingWhileShowingUp = false;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service =
- peekServiceForUserWithLocalBinderIdentityLocked(userId);
- if (service != null) {
- service.notifyImeAnimationEnd();
- } else if (sVerbose) {
- Slog.v(TAG, "notifyImeAnimationEnd(): no service for "
- + userId);
- }
- }
- }
- };
- windowManagerInternal.setImeInsetsAnimationChangeListener(
- imeInsetsAnimationChangeListener);
- }
}
- public boolean mImeAnimatingWhileShowingUp = false;
- public boolean mIsImeShowing = false;
-
@Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
return Settings.Secure.AUTOFILL_SERVICE;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index eda62334ff39..b39b5b1a7660 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -209,11 +209,6 @@ final class AutofillManagerServiceImpl
private final DisabledInfoCache mDisabledInfoCache;
- // Tracks active session id. There is no guarantee that such a session exists. For eg, if the
- // session is destroyed, the id may no longer be valid. We don't update the state in all the
- // cases.
- private int mActiveSessionId = NO_SESSION;
-
AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState,
@@ -392,7 +387,6 @@ final class AutofillManagerServiceImpl
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@NonNull ComponentName clientActivity, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
- mActiveSessionId = NO_SESSION;
// FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled
// but the package is allowlisted for augmented autofill
boolean forAugmentedAutofillOnly = (flags
@@ -451,7 +445,6 @@ final class AutofillManagerServiceImpl
if (newSession == null) {
return NO_SESSION;
}
- mActiveSessionId = newSession.id;
// Service can be null when it's only for augmented autofill
String servicePackageName = mInfo == null ? null : mInfo.getServiceInfo().packageName;
@@ -755,7 +748,6 @@ final class AutofillManagerServiceImpl
Slog.d(TAG, "restarting session " + sessionId + " due to manual request on "
+ autofillId);
}
- mActiveSessionId = sessionId;
return true;
}
if (sVerbose) {
@@ -765,8 +757,6 @@ final class AutofillManagerServiceImpl
return false;
}
-
- mActiveSessionId = sessionId;
session.updateLocked(autofillId, virtualBounds, value, action, flags);
return false;
}
@@ -886,54 +876,21 @@ final class AutofillManagerServiceImpl
}
@GuardedBy("mLock")
- public void notifyImeAnimationStart() {
- if (!isEnabledLocked()) {
- Slog.wtf(TAG, "Service not enabled");
- return;
- }
- final Session session = mSessions.get(mActiveSessionId);
- if (session == null) {
- Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
- return;
- }
- session.notifyImeAnimationStart(SystemClock.elapsedRealtime());
- }
-
- @GuardedBy("mLock")
public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
if (!isEnabledLocked()) {
Slog.wtf(TAG, "Service not enabled");
return;
}
final Session session = mSessions.get(sessionId);
- if (session == null) {
+ if (session == null || uid != session.uid) {
Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+ sessionId + "(" + uid + ")");
return;
}
- if (uid != session.uid) {
- Slog.v(TAG, "notifyImeAnimationEnd(): Mismatched session id's "
- + sessionId + "(" + uid + ")");
- return;
- }
session.notifyImeAnimationEnd(endTimeMs);
}
@GuardedBy("mLock")
- public void notifyImeAnimationEnd() {
- if (!isEnabledLocked()) {
- Slog.wtf(TAG, "Service not enabled");
- return;
- }
- final Session session = mSessions.get(mActiveSessionId);
- if (session == null) {
- Slog.v(TAG, "notifyImeAnimationEnd(): no session for " + mActiveSessionId);
- return;
- }
- session.notifyImeAnimationEnd(SystemClock.elapsedRealtime());
- }
-
- @GuardedBy("mLock")
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3817ba1a28b9..2c0366e6a6db 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,11 +24,14 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.window.flags.Flags.balClearAllowlistDuration;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -305,6 +308,10 @@ 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) {
@@ -323,6 +330,13 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
mAllowBgActivityStartsForActivitySender.remove(token);
mAllowBgActivityStartsForBroadcastSender.remove(token);
mAllowBgActivityStartsForServiceSender.remove(token);
+ if (mAllowlistDuration != null && balClearAllowlistDuration()) {
+ TempAllowListDuration duration = mAllowlistDuration.get(token);
+ if (duration != null
+ && duration.type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ duration.type = TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+ }
+ }
}
public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -703,7 +717,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
return res;
}
- private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+ @VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
IBinder allowlistToken) {
return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
diff --git a/services/core/java/com/android/server/display/plugin/PluginManager.java b/services/core/java/com/android/server/display/plugin/PluginManager.java
index d4099975cafa..cb0a4574361a 100644
--- a/services/core/java/com/android/server/display/plugin/PluginManager.java
+++ b/services/core/java/com/android/server/display/plugin/PluginManager.java
@@ -74,15 +74,17 @@ public class PluginManager {
/**
* Adds change listener for particular plugin type
*/
- public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.addListener(type, listener);
+ public <T> void subscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.addListener(type, uniqueDisplayId, listener);
}
/**
* Removes change listener
*/
- public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.removeListener(type, listener);
+ public <T> void unsubscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.removeListener(type, uniqueDisplayId, listener);
}
/**
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index dd3415fb614d..5102c2709329 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -20,10 +20,13 @@ import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -35,42 +38,97 @@ import java.util.Set;
public class PluginStorage {
private static final String TAG = "PluginStorage";
+ // Special ID used to indicate that given value is to be applied globally, rather than to a
+ // specific display. If both GLOBAL and specific display values are present - specific display
+ // value is selected.
+ @VisibleForTesting
+ static final String GLOBAL_ID = "GLOBAL";
+
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final Map<PluginType<?>, Object> mValues = new HashMap<>();
+ private final Map<PluginType<?>, ValuesContainer<?>> mValues = new HashMap<>();
@GuardedBy("mLock")
private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
@GuardedBy("mLock")
- private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
+ private final Map<String, PluginEventStorage> mPluginEventStorages = new HashMap<>();
+
+ /**
+ * Updates value in storage and forwards it to corresponding listeners for all displays
+ * that does not have display specific value.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ */
+ @KeepForApi
+ public <T> void updateGlobalValue(PluginType<T> type, @Nullable T value) {
+ updateValue(type, GLOBAL_ID, value);
+ }
/**
- * Updates value in storage and forwards it to corresponding listeners.
- * Should be called by OEM Plugin implementation in order to provide communicate with Framework
+ * Updates value in storage and forwards it to corresponding listeners for specific display.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ * @param type - plugin type, that need to be updated
+ * @param uniqueDisplayId - uniqueDisplayId that this type/value should be applied to
+ * @param value - plugin value for particular type and display
*/
@KeepForApi
- public <T> void updateValue(PluginType<T> type, @Nullable T value) {
- Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value);
+ public <T> void updateValue(PluginType<T> type, String uniqueDisplayId, @Nullable T value) {
+ Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value
+ + "; displayId=" + uniqueDisplayId);
Set<PluginManager.PluginChangeListener<T>> localListeners;
+ T valueToNotify;
synchronized (mLock) {
- mValues.put(type, value);
- mPluginEventStorage.onValueUpdated(type);
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- localListeners = new LinkedHashSet<>(container.mListeners);
+ ValuesContainer<T> valuesByType = getValuesContainerLocked(type);
+ valuesByType.updateValueLocked(uniqueDisplayId, value);
+ // if value was set to null, we might need to notify with GLOBAL value instead
+ valueToNotify = valuesByType.getValueLocked(uniqueDisplayId);
+
+ PluginEventStorage storage = mPluginEventStorages.computeIfAbsent(uniqueDisplayId,
+ d -> new PluginEventStorage());
+ storage.onValueUpdated(type);
+
+ localListeners = getListenersForUpdateLocked(type, uniqueDisplayId);
}
Slog.d(TAG, "updateValue, notifying listeners=" + localListeners);
- localListeners.forEach(l -> l.onChanged(value));
+ localListeners.forEach(l -> l.onChanged(valueToNotify));
+ }
+
+ @GuardedBy("mLock")
+ private <T> Set<PluginManager.PluginChangeListener<T>> getListenersForUpdateLocked(
+ PluginType<T> type, String uniqueDisplayId) {
+ ListenersContainer<T> listenersContainer = getListenersContainerLocked(type);
+ Set<PluginManager.PluginChangeListener<T>> localListeners = new LinkedHashSet<>();
+ // if GLOBAL value change we need to notify only listeners for displays that does not
+ // have display specific value
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ Set<String> excludedDisplayIds = valuesContainer.getNonGlobalDisplaysLocked();
+ listenersContainer.mListeners.forEach((localDisplayId, listeners) -> {
+ if (!excludedDisplayIds.contains(localDisplayId)) {
+ localListeners.addAll(listeners);
+ }
+ });
+ } else {
+ localListeners.addAll(
+ listenersContainer.mListeners.getOrDefault(uniqueDisplayId, Set.of()));
+ }
+ return localListeners;
}
/**
* Adds listener for PluginType. If storage already has value for this type, listener will
* be notified immediately.
*/
- <T> void addListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void addListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "addListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
T value = null;
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- if (container.mListeners.add(listener)) {
- value = getValueForTypeLocked(type);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ if (container.addListenerLocked(uniqueDisplayId, listener)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ value = valuesContainer.getValueLocked(uniqueDisplayId);
}
}
if (value != null) {
@@ -81,10 +139,15 @@ public class PluginStorage {
/**
* Removes listener
*/
- <T> void removeListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void removeListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "removeListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- container.mListeners.remove(listener);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ container.removeListenerLocked(uniqueDisplayId, listener);
}
}
@@ -92,53 +155,106 @@ public class PluginStorage {
* Print the object's state and debug information into the given stream.
*/
void dump(PrintWriter pw) {
- Map<PluginType<?>, Object> localValues;
+ Map<PluginType<?>, Map<String, Object>> localValues = new HashMap<>();
@SuppressWarnings("rawtypes")
- Map<PluginType, Set> localListeners = new HashMap<>();
- List<PluginEventStorage.TimeFrame> timeFrames;
+ Map<PluginType, Map<String, Set>> localListeners = new HashMap<>();
+ Map<String, List<PluginEventStorage.TimeFrame>> timeFrames = new HashMap<>();
synchronized (mLock) {
- timeFrames = mPluginEventStorage.getTimeFrames();
- localValues = new HashMap<>(mValues);
- mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
+ mPluginEventStorages.forEach((displayId, storage) -> {
+ timeFrames.put(displayId, storage.getTimeFrames());
+ });
+ mValues.forEach((type, valueContainer) -> {
+ localValues.put(type, new HashMap<>(valueContainer.mValues));
+ });
+ mListeners.forEach((type, container) -> {
+ localListeners.put(type, new HashMap<>(container.mListeners));
+ });
}
pw.println("PluginStorage:");
pw.println("values=" + localValues);
pw.println("listeners=" + localListeners);
pw.println("PluginEventStorage:");
- for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
- timeFrame.dump(pw);
+ for (Map.Entry<String, List<PluginEventStorage.TimeFrame>> timeFrameEntry :
+ timeFrames.entrySet()) {
+ pw.println("TimeFrames for displayId=" + timeFrameEntry.getKey());
+ for (PluginEventStorage.TimeFrame timeFrame : timeFrameEntry.getValue()) {
+ timeFrame.dump(pw);
+ }
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> T getValueForTypeLocked(PluginType<T> type) {
- Object value = mValues.get(type);
- if (value == null) {
- return null;
- } else if (type.mType == value.getClass()) {
- return (T) value;
+ private <T> ListenersContainer<T> getListenersContainerLocked(PluginType<T> type) {
+ ListenersContainer<?> container = mListeners.get(type);
+ if (container == null) {
+ ListenersContainer<T> lc = new ListenersContainer<>();
+ mListeners.put(type, lc);
+ return lc;
} else {
- Slog.d(TAG, "getValueForType: unexpected value type=" + value.getClass().getName()
- + ", expected=" + type.mType.getName());
- return null;
+ return (ListenersContainer<T>) container;
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> ListenersContainer<T> getListenersContainerForTypeLocked(PluginType<T> type) {
- ListenersContainer<?> container = mListeners.get(type);
+ private <T> ValuesContainer<T> getValuesContainerLocked(PluginType<T> type) {
+ ValuesContainer<?> container = mValues.get(type);
if (container == null) {
- ListenersContainer<T> lc = new ListenersContainer<>();
- mListeners.put(type, lc);
- return lc;
+ ValuesContainer<T> vc = new ValuesContainer<>();
+ mValues.put(type, vc);
+ return vc;
} else {
- return (ListenersContainer<T>) container;
+ return (ValuesContainer<T>) container;
}
}
private static final class ListenersContainer<T> {
- private final Set<PluginManager.PluginChangeListener<T>> mListeners = new LinkedHashSet<>();
+ private final Map<String, Set<PluginManager.PluginChangeListener<T>>> mListeners =
+ new LinkedHashMap<>();
+
+ private boolean addListenerLocked(
+ String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay =
+ mListeners.computeIfAbsent(uniqueDisplayId, k -> new LinkedHashSet<>());
+ return listenersForDisplay.add(listener);
+ }
+
+ private void removeListenerLocked(String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay = mListeners.get(
+ uniqueDisplayId);
+ if (listenersForDisplay == null) {
+ return;
+ }
+
+ listenersForDisplay.remove(listener);
+
+ if (listenersForDisplay.isEmpty()) {
+ mListeners.remove(uniqueDisplayId);
+ }
+ }
+ }
+
+ private static final class ValuesContainer<T> {
+ private final Map<String, T> mValues = new HashMap<>();
+
+ private void updateValueLocked(String uniqueDisplayId, @Nullable T value) {
+ if (value == null) {
+ mValues.remove(uniqueDisplayId);
+ } else {
+ mValues.put(uniqueDisplayId, value);
+ }
+ }
+
+ private Set<String> getNonGlobalDisplaysLocked() {
+ Set<String> keys = new HashSet<>(mValues.keySet());
+ keys.remove(GLOBAL_ID);
+ return keys;
+ }
+
+ private @Nullable T getValueLocked(String displayId) {
+ return mValues.getOrDefault(displayId, mValues.get(GLOBAL_ID));
+ }
}
}
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
index 4fea05d295b6..0f0d382e28f8 100644
--- a/services/core/java/com/android/server/infra/OWNERS
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 655446
srazdan@google.com
+reemabajwa@google.com
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index b9352edf9230..ddace179348c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -42,6 +42,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import java.util.Collection;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -372,10 +373,12 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
/* package */ void onEndpointSessionOpenRequest(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
- boolean success =
+ Optional<Byte> error =
onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
- if (!success) {
- cleanupSessionResources(sessionId);
+ if (error.isPresent()) {
+ halCloseEndpointSessionNoThrow(sessionId, error.get());
+ onCloseEndpointSession(sessionId, error.get());
+ // Resource cleanup is done in onCloseEndpointSession
}
}
@@ -422,7 +425,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
- private boolean onEndpointSessionOpenRequestInternal(
+ private Optional<Byte> onEndpointSessionOpenRequestInternal(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
if (!hasEndpointPermissions(initiator)) {
Log.e(
@@ -431,22 +434,21 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ initiator
+ " doesn't have permission for "
+ mEndpointInfo);
- halCloseEndpointSessionNoThrow(sessionId, Reason.PERMISSION_DENIED);
- return false;
+ return Optional.of(Reason.PERMISSION_DENIED);
}
synchronized (mOpenSessionLock) {
if (hasSessionId(sessionId)) {
Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
- halCloseEndpointSessionNoThrow(sessionId, Reason.UNSPECIFIED);
- return false;
+ return Optional.of(Reason.UNSPECIFIED);
}
mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
}
- return invokeCallback(
+ boolean success = invokeCallback(
(consumer) ->
consumer.onSessionOpenRequest(sessionId, initiator, serviceDescriptor));
+ return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
}
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index 2e0bb4f88485..18f2f48b80a3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyCallback;
@@ -38,6 +39,7 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -60,21 +62,35 @@ public class MediaProjectionStopController {
private final TelephonyManager mTelephonyManager;
private final AppOpsManager mAppOpsManager;
private final PackageManager mPackageManager;
- private final RoleManager mRoleManager;
+ private final RoleHolderProvider mRoleHolderProvider;
private final ContentResolver mContentResolver;
private boolean mIsInCall;
private long mLastCallStartTimeMillis;
+
+ @VisibleForTesting
+ interface RoleHolderProvider {
+ List<String> getRoleHoldersAsUser(String roleName, UserHandle user);
+ }
+
public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
+ this(context, stopReasonConsumer,
+ (roleName, user) -> context.getSystemService(RoleManager.class)
+ .getRoleHoldersAsUser(roleName, user));
+ }
+
+ @VisibleForTesting
+ MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer,
+ RoleHolderProvider roleHolderProvider) {
mStopReasonConsumer = stopReasonConsumer;
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mTelecomManager = context.getSystemService(TelecomManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mPackageManager = context.getPackageManager();
- mRoleManager = context.getSystemService(RoleManager.class);
mContentResolver = context.getContentResolver();
+ mRoleHolderProvider = roleHolderProvider;
}
/**
@@ -146,8 +162,9 @@ public class MediaProjectionStopController {
Slog.v(TAG, "Continuing MediaProjection for package with OP_PROJECT_MEDIA AppOp ");
return true;
}
- if (mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- projectionGrant.userHandle).contains(projectionGrant.packageName)) {
+ if (mRoleHolderProvider.getRoleHoldersAsUser(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING, projectionGrant.userHandle)
+ .contains(projectionGrant.packageName)) {
Slog.v(TAG, "Continuing MediaProjection for package holding app streaming role.");
return true;
}
@@ -177,10 +194,6 @@ public class MediaProjectionStopController {
*/
public boolean isStartForbidden(
MediaProjectionManagerService.MediaProjection projectionGrant) {
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return false;
- }
-
if (!mKeyguardManager.isKeyguardLocked()) {
return false;
}
@@ -194,9 +207,6 @@ public class MediaProjectionStopController {
@VisibleForTesting
void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
if (!isKeyguardLocked) return;
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return;
- }
mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0d3c18ac339f..b4a58ac72394 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5056,14 +5056,8 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public ParceledListSlice<NotificationChannel> getNotificationChannels(
- String callingPkg, String targetPkg, int userId) {
- return getOrCreateNotificationChannels(callingPkg, targetPkg, userId, false);
- }
-
- @Override
- public ParceledListSlice<NotificationChannel> getOrCreateNotificationChannels(
- String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded) {
+ public ParceledListSlice<NotificationChannel> getNotificationChannels(String callingPkg,
+ String targetPkg, int userId) {
if (canNotifyAsPackage(callingPkg, targetPkg, userId)
|| isCallingUidSystem()) {
int targetUid = -1;
@@ -5073,8 +5067,7 @@ public class NotificationManagerService extends SystemService {
/* ignore */
}
return mPreferencesHelper.getNotificationChannels(
- targetPkg, targetUid, false /* includeDeleted */, true,
- createPrefsIfNeeded);
+ targetPkg, targetUid, false /* includeDeleted */, true);
}
throw new SecurityException("Pkg " + callingPkg
+ " cannot read channels for " + targetPkg + " in " + userId);
@@ -8505,6 +8498,9 @@ public class NotificationManagerService extends SystemService {
(userId == USER_ALL) ? USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
+ // can't be set by an app
+ notification.extras.remove(Notification.EXTRA_SUMMARIZED_CONTENT);
+
if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
notification.flags &= ~FLAG_FOREGROUND_SERVICE;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3b34dcd17705..14cc91b6305f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,8 +16,8 @@
package com.android.server.notification;
-import static android.app.Flags.notificationClassificationUi;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.Flags.notificationClassificationUi;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
@@ -89,9 +89,10 @@ import android.util.SparseBooleanArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
@@ -1962,21 +1963,11 @@ public class PreferencesHelper implements RankingConfig {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
boolean includeDeleted, boolean includeBundles) {
- return getNotificationChannels(pkg, uid, includeDeleted, includeBundles, false);
- }
-
- protected ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
- boolean includeDeleted, boolean includeBundles, boolean createPrefsIfNeeded) {
- if (createPrefsIfNeeded && !android.app.Flags.nmBinderPerfCacheChannels()) {
- Slog.wtf(TAG,
- "getNotificationChannels called with createPrefsIfNeeded=true and flag off");
- createPrefsIfNeeded = false;
- }
Objects.requireNonNull(pkg);
List<NotificationChannel> channels = new ArrayList<>();
synchronized (mLock) {
PackagePreferences r;
- if (createPrefsIfNeeded) {
+ if (android.app.Flags.nmBinderPerfCacheChannels()) {
r = getOrCreatePackagePreferencesLocked(pkg, uid);
} else {
r = getPackagePreferencesLocked(pkg, uid);
@@ -1997,6 +1988,18 @@ public class PreferencesHelper implements RankingConfig {
}
}
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ // Gets the entire list of notification channels for this package, with no filtering and
+ // without creating package preferences. For testing only, specifically to confirm the
+ // notification channels of a removed/deleted package.
+ protected List<NotificationChannel> getRemovedPkgNotificationChannels(String pkg, int uid) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null || r.channels == null) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(r.channels.values());
+ }
+
/**
* Gets all notification channels associated with the given pkg and uid that can bypass dnd
*/
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 977c6db66106..a5185a2139db 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -38,6 +38,7 @@ import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.internal.util.ArrayUtils;
+import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.BatteryStatsImpl.BatteryStatsSession;
import java.io.PrintWriter;
@@ -351,7 +352,7 @@ public class BatteryUsageStatsProvider {
accumulatedStats.endMonotonicTime = endMonotonicTime;
accumulatedStats.builder.setStatsEndTimestamp(endWallClockTime);
- accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
+ accumulatedStats.builder.setStatsDuration(endMonotonicTime - startMonotonicTime);
mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, session.getHistory(),
startMonotonicTime, endMonotonicTime);
@@ -403,7 +404,10 @@ public class BatteryUsageStatsProvider {
}
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
- batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy());
+ batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy(),
+ Flags.extendedBatteryHistoryContinuousCollectionEnabled()
+ ? query.getPreferredHistoryDurationMs()
+ : Long.MAX_VALUE);
}
mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, session.getHistory(),
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 064ef1aa0eff..89b46bc4eba4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,6 +46,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -8376,6 +8377,7 @@ final class ActivityRecord extends WindowToken {
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+ // TODO(b/392069771): Move to AppCompatSandboxingPolicy.
// Sandbox max bounds by setting it to the activity bounds, if activity is letterboxed, or
// has or will have mAppCompatDisplayInsets for size compat. Also forces an activity to be
// sandboxed or not depending upon the configuration settings.
@@ -8404,6 +8406,20 @@ final class ActivityRecord extends WindowToken {
resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
}
+ // Sandbox activity bounds in freeform to app bounds to force app to display within the
+ // container. This prevents UI cropping when activities can draw below insets which are
+ // normally excluded from appBounds before targetSDK < 35
+ // (see ConfigurationContainer#applySizeOverrideIfNeeded).
+ if (isFloating(parentWindowingMode)) {
+ Rect appBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ if (appBounds == null || appBounds.isEmpty()) {
+ // When there is no override bounds, the activity will inherit the bounds from
+ // parent.
+ appBounds = mResolveConfigHint.mParentAppBoundsOverride;
+ }
+ resolvedConfig.windowConfiguration.setBounds(appBounds);
+ }
+
applySizeOverrideIfNeeded(
mDisplayContent,
info.applicationInfo,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7d4467a6e98..81c7807311dd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -71,8 +71,6 @@ import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -91,9 +89,7 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -157,8 +153,6 @@ import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import com.android.wm.shell.Flags;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.text.DateFormat;
import java.util.Date;
import java.util.function.Supplier;
@@ -172,8 +166,6 @@ import java.util.function.Supplier;
class ActivityStarter {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_ATM;
private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
- private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
private static final int INVALID_LAUNCH_MODE = -1;
@@ -255,26 +247,7 @@ class ActivityStarter {
private boolean mIsTaskCleared;
private boolean mMovedToFront;
private boolean mNoAnimation;
-
- // TODO mAvoidMoveToFront before V is changed from a boolean to a int code mCanMoveToFrontCode
- // for the purpose of attribution of new BAL V feature. This should be reverted back to the
- // boolean flag post V.
- @IntDef(prefix = {"MOVE_TO_FRONT_"}, value = {
- MOVE_TO_FRONT_ALLOWED,
- MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS,
- MOVE_TO_FRONT_AVOID_LEGACY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MoveToFrontCode {}
-
- // Allows a task move to front.
- private static final int MOVE_TO_FRONT_ALLOWED = 0;
- // Avoid a task move to front because the Pending Intent that starts the activity only
- // its creator has the BAL privilege, its sender does not.
- private static final int MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS = 1;
- // Avoid a task move to front because of all other legacy reasons.
- private static final int MOVE_TO_FRONT_AVOID_LEGACY = 2;
- private @MoveToFrontCode int mCanMoveToFrontCode = MOVE_TO_FRONT_ALLOWED;
+ private boolean mAvoidMoveToFront;
private boolean mFrozeTaskList;
private boolean mTransientLaunch;
// The task which was above the targetTask before starting this activity. null if the targetTask
@@ -771,7 +744,7 @@ class ActivityStarter {
mIsTaskCleared = starter.mIsTaskCleared;
mMovedToFront = starter.mMovedToFront;
mNoAnimation = starter.mNoAnimation;
- mCanMoveToFrontCode = starter.mCanMoveToFrontCode;
+ mAvoidMoveToFront = starter.mAvoidMoveToFront;
mFrozeTaskList = starter.mFrozeTaskList;
mVoiceSession = starter.mVoiceSession;
@@ -1711,14 +1684,6 @@ class ActivityStarter {
return result;
}
- private boolean avoidMoveToFront() {
- return mCanMoveToFrontCode != MOVE_TO_FRONT_ALLOWED;
- }
-
- private boolean avoidMoveToFrontPIOnlyCreatorAllows() {
- return mCanMoveToFrontCode == MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS;
- }
-
/**
* If the start result is success, ensure that the configuration of the started activity matches
* the current display. Otherwise clean up unassociated containers to avoid leakage.
@@ -1768,7 +1733,7 @@ class ActivityStarter {
startedActivityRootTask.setAlwaysOnTop(true);
}
- if (isIndependentLaunch && !mDoResume && avoidMoveToFront() && !mTransientLaunch
+ if (isIndependentLaunch && !mDoResume && mAvoidMoveToFront && !mTransientLaunch
&& !started.shouldBeVisible(true /* ignoringKeyguard */)) {
Slog.i(TAG, "Abort " + transition + " of invisible launch " + started);
transition.abort();
@@ -1784,7 +1749,7 @@ class ActivityStarter {
currentTop, currentTop.mDisplayContent, false /* deferResume */);
}
- if (!avoidMoveToFront() && mDoResume
+ if (!mAvoidMoveToFront && mDoResume
&& !mService.getUserManagerInternal().isVisibleBackgroundFullUser(started.mUserId)
&& mRootWindowContainer.hasVisibleWindowAboveButDoesNotOwnNotificationShade(
started.launchedFromUid)) {
@@ -1934,19 +1899,17 @@ class ActivityStarter {
}
// When running transient transition, the transient launch target should keep on top.
// So disallow the transient hide activity to move itself to front, e.g. trampoline.
- if (!avoidMoveToFront() && (mService.mHomeProcess == null
+ if (!mAvoidMoveToFront && (mService.mHomeProcess == null
|| mService.mHomeProcess.mUid != realCallingUid)
&& (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents())
&& r.mTransitionController.isTransientHide(targetTask)) {
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
+ mAvoidMoveToFront = true;
}
// If the activity is started by sending a pending intent and only its creator has the
// privilege to allow BAL (its sender does not), avoid move it to the front. Only do
// this when it is not a new task and not already been marked as avoid move to front.
- // Guarded by a flag: balDontBringExistingBackgroundTaskStackToFg
- if (balDontBringExistingBackgroundTaskStackToFg() && !avoidMoveToFront()
- && balVerdict.onlyCreatorAllows()) {
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS;
+ if (!mAvoidMoveToFront && balVerdict.onlyCreatorAllows()) {
+ mAvoidMoveToFront = true;
}
mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask());
}
@@ -2003,32 +1966,28 @@ class ActivityStarter {
// After activity is attached to task, but before actual start
recordTransientLaunchIfNeeded(mLastStartActivityRecord);
- if (mDoResume) {
- if (!avoidMoveToFront()) {
- mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
-
- final boolean launchBehindDream;
- if (com.android.window.flags.Flags.removeActivityStarterDreamCallback()) {
- final TaskDisplayArea tda = mTargetRootTask.getTaskDisplayArea();
- final Task top = (tda != null ? tda.getTopRootTask() : null);
- launchBehindDream = (top != null && top != mTargetRootTask)
- && top.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_DREAM
- && top.getTopNonFinishingActivity() != null;
- } else {
- launchBehindDream = !mTargetRootTask.isTopRootTaskInDisplayArea()
- && mService.isDreaming()
- && !dreamStopping;
- }
+ if (!mAvoidMoveToFront && mDoResume) {
+ mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
- if (launchBehindDream) {
- // Launching underneath dream activity (fullscreen, always-on-top). Run the
- // launch--behind transition so the Activity gets created and starts
- // in visible state.
- mLaunchTaskBehind = true;
- r.mLaunchTaskBehind = true;
- }
+ final boolean launchBehindDream;
+ if (com.android.window.flags.Flags.removeActivityStarterDreamCallback()) {
+ final TaskDisplayArea tda = mTargetRootTask.getTaskDisplayArea();
+ final Task top = (tda != null ? tda.getTopRootTask() : null);
+ launchBehindDream = (top != null && top != mTargetRootTask)
+ && top.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_DREAM
+ && top.getTopNonFinishingActivity() != null;
} else {
- logPIOnlyCreatorAllowsBAL();
+ launchBehindDream = !mTargetRootTask.isTopRootTaskInDisplayArea()
+ && mService.isDreaming()
+ && !dreamStopping;
+ }
+
+ if (launchBehindDream) {
+ // Launching underneath dream activity (fullscreen, always-on-top). Run the
+ // launch--behind transition so the Activity gets created and starts
+ // in visible state.
+ mLaunchTaskBehind = true;
+ r.mLaunchTaskBehind = true;
}
}
@@ -2089,13 +2048,9 @@ class ActivityStarter {
// root-task to the will not update the focused root-task. If starting the new
// activity now allows the task root-task to be focusable, then ensure that we
// now update the focused root-task accordingly.
- if (mTargetRootTask.isTopActivityFocusable()
+ if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()
&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
- if (!avoidMoveToFront()) {
- mTargetRootTask.moveToFront("startActivityInner");
- } else {
- logPIOnlyCreatorAllowsBAL();
- }
+ mTargetRootTask.moveToFront("startActivityInner");
}
mRootWindowContainer.resumeFocusedTasksTopActivities(
mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
@@ -2123,26 +2078,6 @@ class ActivityStarter {
return START_SUCCESS;
}
- // TODO (b/316135632) Post V release, remove this log method.
- private void logPIOnlyCreatorAllowsBAL() {
- if (!avoidMoveToFrontPIOnlyCreatorAllows()) return;
- String realCallingPackage =
- mService.mContext.getPackageManager().getNameForUid(mRealCallingUid);
- if (realCallingPackage == null) {
- realCallingPackage = "uid=" + mRealCallingUid;
- }
- Slog.wtf(TAG, "Without Android 15 BAL hardening this activity would be moved to the "
- + "foreground. The activity is started by a PendingIntent. However, only the "
- + "creator of the PendingIntent allows BAL while the sender does not allow BAL. "
- + "realCallingPackage: " + realCallingPackage
- + "; callingPackage: " + mRequest.callingPackage
- + "; mTargetRootTask:" + mTargetRootTask
- + "; mIntent: " + mIntent
- + "; mTargetRootTask.getTopNonFinishingActivity: "
- + mTargetRootTask.getTopNonFinishingActivity()
- + "; mTargetRootTask.getRootActivity: " + mTargetRootTask.getRootActivity());
- }
-
private void recordTransientLaunchIfNeeded(ActivityRecord r) {
if (r == null || !mTransientLaunch) return;
final TransitionController controller = r.mTransitionController;
@@ -2287,7 +2222,7 @@ class ActivityStarter {
}
if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
- mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
+ mSourceRecord, r, newTask, mAvoidMoveToFront, targetTask, mLaunchFlags, mBalCode,
mCallingUid, mRealCallingUid, mPreferredTaskDisplayArea)) {
return START_ABORTED;
}
@@ -2635,7 +2570,7 @@ class ActivityStarter {
mIsTaskCleared = false;
mMovedToFront = false;
mNoAnimation = false;
- mCanMoveToFrontCode = MOVE_TO_FRONT_ALLOWED;
+ mAvoidMoveToFront = false;
mFrozeTaskList = false;
mTransientLaunch = false;
mPriorAboveTask = null;
@@ -2747,12 +2682,12 @@ class ActivityStarter {
// The caller specifies that we'd like to be avoided to be moved to the
// front, so be it!
mDoResume = false;
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
+ mAvoidMoveToFront = true;
}
}
} else if (mOptions.getAvoidMoveToFront()) {
mDoResume = false;
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
+ mAvoidMoveToFront = true;
}
mTransientLaunch = mOptions.getTransientLaunch();
final KeyguardController kc = mSupervisor.getKeyguardController();
@@ -2762,7 +2697,7 @@ class ActivityStarter {
if (mTransientLaunch && mDisplayLockAndOccluded
&& mService.getTransitionController().isShellTransitionsEnabled()) {
mDoResume = false;
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
+ mAvoidMoveToFront = true;
}
mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
@@ -2819,7 +2754,7 @@ class ActivityStarter {
mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
if (mBalCode == BAL_BLOCK && !mService.isBackgroundActivityStartsEnabled()) {
- mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
+ mAvoidMoveToFront = true;
mDoResume = false;
}
}
@@ -3050,7 +2985,7 @@ class ActivityStarter {
differentTopTask = true;
}
- if (differentTopTask && !avoidMoveToFront()) {
+ if (differentTopTask && !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
@@ -3094,9 +3029,6 @@ class ActivityStarter {
}
mOptions = null;
}
- if (differentTopTask) {
- logPIOnlyCreatorAllowsBAL();
- }
// Update the target's launch cookie and pending remote animation to those specified in the
// options if set.
if (mStartActivity.mLaunchCookie != null) {
@@ -3137,7 +3069,7 @@ class ActivityStarter {
}
private void setNewTask(Task taskToAffiliate) {
- final boolean toTop = !mLaunchTaskBehind && !avoidMoveToFront();
+ final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
final Task task = mTargetRootTask.reuseOrCreateTask(
mStartActivity.info, mIntent, mVoiceSession,
mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 35fa39dab900..d994a1904a14 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -180,10 +180,7 @@ class AppCompatOrientationPolicy {
return true;
}
- final AppCompatCameraPolicy cameraPolicy = AppCompatCameraPolicy
- .getAppCompatCameraPolicy(mActivityRecord);
- if (cameraPolicy != null
- && cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
+ if (AppCompatCameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
Slog.w(TAG, "Ignoring orientation update to "
+ screenOrientationToString(requestedOrientation)
+ " due to camera compat treatment for " + mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 119709e86551..6df01f4b328b 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -45,12 +45,11 @@ import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONL
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static com.android.window.flags.Flags.balAdditionalStartModes;
-import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
import static com.android.window.flags.Flags.balShowToastsBlocked;
-import static com.android.window.flags.Flags.balStrictModeRo;
import static com.android.window.flags.Flags.balStrictModeGracePeriod;
+import static com.android.window.flags.Flags.balStrictModeRo;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Objects.requireNonNull;
@@ -620,8 +619,6 @@ public class BackgroundActivityStartController {
// features
sb.append("; balRequireOptInByPendingIntentCreator: ")
.append(balRequireOptInByPendingIntentCreator());
- sb.append("; balDontBringExistingBackgroundTaskStackToFg: ")
- .append(balDontBringExistingBackgroundTaskStackToFg());
sb.append("]");
return sb.toString();
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f465c95addb7..4bcba13448e9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -899,9 +899,7 @@ class InsetsPolicy {
}
@Override
- public void notifyAnimationRunningStateChanged(boolean running,
- @InsetsController.AnimationType int animationType,
- @InsetsType int insetsTypes) {
+ public void notifyAnimationRunningStateChanged(boolean running) {
mInsetsAnimationRunning = running;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e761e024b3ca..883d8f95b612 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1680,26 +1680,27 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Sets the specified orientation of this container. It percolates this change upward along the
* hierarchy to let each level of the hierarchy a chance to respond to it.
*
- * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
+ * @param requestedOrientation the specified orientation. Needs to be one of
+ * {@link ScreenOrientation}.
* @param requestingContainer the container which orientation request has changed. Mostly used
* to ensure it gets correct configuration.
* @return the resolved override orientation of this window container.
*/
@ScreenOrientation
- int setOrientation(@ScreenOrientation int orientation,
+ int setOrientation(@ScreenOrientation int requestedOrientation,
@Nullable WindowContainer requestingContainer) {
- if (getOverrideOrientation() == orientation) {
- return orientation;
+ if (getOverrideOrientation() == requestedOrientation) {
+ return requestedOrientation;
}
- setOverrideOrientation(orientation);
+ setOverrideOrientation(requestedOrientation);
final WindowContainer parent = getParent();
if (parent == null) {
- return orientation;
+ return requestedOrientation;
}
// The derived class can return a result that is different from the given orientation.
- final int resolvedOrientation = getOverrideOrientation();
+ final int actualOverrideOrientation = getOverrideOrientation();
if (getConfiguration().orientation != getRequestedConfigurationOrientation(
- false /* forDisplay */, resolvedOrientation)
+ false /* forDisplay */, actualOverrideOrientation)
// Update configuration directly only if the change won't be dispatched from
// ancestor. This prevents from computing intermediate configuration when the
// parent also needs to be updated from the ancestor. E.g. the app requests
@@ -1707,12 +1708,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// the task can be updated to portrait first so the configuration can be
// computed in a consistent environment.
&& (inMultiWindowMode()
- || !handlesOrientationChangeFromDescendant(orientation))) {
+ || !handlesOrientationChangeFromDescendant(requestedOrientation))) {
// Resolve the requested orientation.
onConfigurationChanged(parent.getConfiguration());
}
onDescendantOrientationChanged(requestingContainer);
- return resolvedOrientation;
+ return actualOverrideOrientation;
}
@ScreenOrientation
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 7a8230f1f963..c77b1d9a7bcf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -41,7 +41,6 @@ import android.view.Display;
import android.view.IInputFilter;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
-import android.view.InsetsController;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
@@ -470,24 +469,6 @@ public abstract class WindowManagerInternal {
public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
/**
- * Set by the autofill service to observe changes in the ime animations.
- *
- * @param listener The callbacks to invoke.
- */
- public abstract void setImeInsetsAnimationChangeListener(
- @Nullable ImeInsetsAnimationChangeListener listener);
-
- /** Listener for changes in ime insets animation */
- public interface ImeInsetsAnimationChangeListener {
-
- /** Notify on start of animation */
- void onAnimationStart(@InsetsController.AnimationType int animationType, int userId);
-
- /** Notify on end of animation */
- void onAnimationEnd(@InsetsController.AnimationType int animationType, int userId);
- }
-
- /**
* Sets a callback for observing which windows are touchable for the purposes
* of accessibility on specified display.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 28ea3b0bf6ba..3a1d652f82d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INPUT_CONSUMER;
@@ -278,7 +277,6 @@ import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
-import android.view.InsetsController;
import android.view.InsetsFrameProvider;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -795,9 +793,6 @@ public class WindowManagerService extends IWindowManager.Stub
final TrustedPresentationListenerController mTrustedPresentationListenerController =
new TrustedPresentationListenerController();
- private WindowManagerInternal.ImeInsetsAnimationChangeListener
- mImeInsetsAnimationChangeListener;
-
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
private final Uri mDisplayInversionEnabledUri =
@@ -8627,14 +8622,6 @@ public class WindowManagerService extends IWindowManager.Stub
// WMS.takeAssistScreenshot takes care of the locking.
return WindowManagerService.this.takeAssistScreenshot(windowTypesToExclude);
}
-
- @Override
- public void setImeInsetsAnimationChangeListener(
- @Nullable WindowManagerInternal.ImeInsetsAnimationChangeListener listener) {
- synchronized (mGlobalLock) {
- mImeInsetsAnimationChangeListener = listener;
- }
- }
}
private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
@@ -10192,24 +10179,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void notifyImeInsetsAnimationStateChanged(
- boolean running, @InsetsController.AnimationType int animationType) {
- if (improveFillDialogAconfig()) {
- synchronized (mGlobalLock) {
- if (mImeInsetsAnimationChangeListener == null) {
- return;
- }
- if (running) {
- mImeInsetsAnimationChangeListener.onAnimationStart(
- animationType, mCurrentUserId);
- } else {
- mImeInsetsAnimationChangeListener.onAnimationEnd(animationType, mCurrentUserId);
- }
- }
- }
- }
-
boolean getDisableSecureWindows() {
return mDisableSecureWindows;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a1755e4d9d3b..060f2e803ec9 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -756,40 +756,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
t.getTaskFragmentOrganizer());
}
}
- // Queue-up bounds-change transactions for tasks which are now organized. Do
- // this after hierarchy ops so we have the final organized state.
- entries = t.getChanges().entrySet().iterator();
- while (entries.hasNext()) {
- final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
- final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on detached container: " + wc);
- continue;
- }
- final Task task = wc.asTask();
- final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
- if (task == null || !task.isAttached() || surfaceBounds == null) {
- continue;
- }
- if (!task.isOrganized()) {
- final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
- // Also allow direct children of created-by-organizer tasks to be
- // controlled. In the future, these will become organized anyways.
- if (parent == null || !parent.mCreatedByOrganizer) {
- throw new IllegalArgumentException(
- "Can't manipulate non-organized task surface " + task);
- }
- }
- final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
- final SurfaceControl sc = task.getSurfaceControl();
- sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
- if (surfaceBounds.isEmpty()) {
- sft.setWindowCrop(sc, null);
- } else {
- sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
- }
- task.setMainWindowSizeChangeTransaction(sft);
- }
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.mTaskSupervisor.endDeferResume();
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 073ee31ddd60..195c65d6ec45 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -23,6 +23,7 @@ import static com.android.internal.util.Preconditions.checkCallAuthorization;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -51,7 +52,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;
import java.io.FileDescriptor;
@@ -62,26 +62,28 @@ import java.util.List;
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
- private final Context mContext;
-
// TODO(b/362756788): Does this need to be a LockGuard lock?
private final Object mLockDoNoUseDirectly = new Object();
@GuardedBy("getLockObject()")
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
- private final DevicePolicyManagerInternal mDpmInternal;
- private final PackageManager mPackageManager;
- private final UserManagerInternal mUserManagerInternal;
+ private final Context mContext;
+ private final Injector mInjector;
+ final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
- mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
- mPackageManager = context.getPackageManager();
- mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
- mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
+ mInjector = new Injector(context);
+ mInjector.getUserManagerInternal().addUserLifecycleListener(new UserLifecycleListener());
}
+ /**
+ * Returns whether supervision is enabled for the given user.
+ *
+ * <p>Supervision is automatically enabled when the supervision app becomes the profile owner or
+ * explicitly enabled via an internal call to {@link #setSupervisionEnabledForUser}.
+ */
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
@@ -92,6 +94,20 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
+ /**
+ * Returns the package name of the active supervision app or null if supervision is disabled.
+ */
+ @Override
+ @Nullable
+ public String getActiveSupervisionAppPackage(@UserIdInt int userId) {
+ if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+ enforcePermission(INTERACT_ACROSS_USERS);
+ }
+ synchronized (getLockObject()) {
+ return getUserDataLocked(userId).supervisionAppPackage;
+ }
+ }
+
@Override
public void onShellCommand(
@Nullable FileDescriptor in,
@@ -114,7 +130,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
pw.println("SupervisionService state:");
pw.increaseIndent();
- List<UserInfo> users = mUserManagerInternal.getUsers(false);
+ List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
synchronized (getLockObject()) {
for (var user : users) {
getUserDataLocked(user.id).dump(pw);
@@ -140,35 +156,54 @@ public class SupervisionService extends ISupervisionManager.Stub {
return data;
}
- void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+ /**
+ * Sets supervision as enabled or disabled for the given user and, in case supervision is being
+ * enabled, the package of the active supervision app.
+ */
+ private void setSupervisionEnabledForUser(
+ @UserIdInt int userId, boolean enabled, @Nullable String supervisionAppPackage) {
synchronized (getLockObject()) {
- getUserDataLocked(userId).supervisionEnabled = enabled;
+ SupervisionUserData data = getUserDataLocked(userId);
+ data.supervisionEnabled = enabled;
+ data.supervisionAppPackage = enabled ? supervisionAppPackage : null;
}
}
- /** Ensures that supervision is enabled when supervision app is the profile owner. */
+ /** Ensures that supervision is enabled when the supervision app is the profile owner. */
private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
- if (isProfileOwner(userId)) {
- setSupervisionEnabledForUser(userId, true);
+ final DevicePolicyManagerInternal dpmInternal = mInjector.getDpmInternal();
+ final ComponentName po =
+ dpmInternal != null ? dpmInternal.getProfileOwnerAsUser(userId) : null;
+
+ if (po != null && po.getPackageName().equals(getSystemSupervisionPackage())) {
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
+ } else if (po != null && po.equals(getSupervisionProfileOwnerComponent())) {
+ // TODO(b/392071637): Consider not enabling supervision in case profile owner is given
+ // to the legacy supervision profile owner component.
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
} else {
// TODO(b/381428475): Avoid disabling supervision when the app is not the profile owner.
// This might only be possible after introducing specific and public APIs to enable
- // supervision.
- setSupervisionEnabledForUser(userId, false);
+ // and disable supervision.
+ setSupervisionEnabledForUser(userId, false, /* supervisionAppPackage= */ null);
}
}
- /** Returns whether the supervision app has profile owner status. */
- private boolean isProfileOwner(@UserIdInt int userId) {
- ComponentName profileOwner =
- mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
- return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+ /**
+ * Returns the {@link ComponentName} of the supervision profile owner component.
+ *
+ * <p>This component is used to give GMS Kids Module permission to supervise the device and may
+ * still be active during the transition to the {@code SYSTEM_SUPERVISION} role.
+ */
+ private ComponentName getSupervisionProfileOwnerComponent() {
+ return ComponentName.unflattenFromString(
+ mContext.getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent));
}
- /** Returns whether the given package name belongs to the supervision role holder. */
- private boolean isSupervisionAppPackage(String packageName) {
- return packageName.equals(
- mContext.getResources().getString(R.string.config_systemSupervision));
+ /** Returns the package assigned to the {@code SYSTEM_SUPERVISION} role. */
+ private String getSystemSupervisionPackage() {
+ return mContext.getResources().getString(R.string.config_systemSupervision);
}
/** Enforces that the caller has the given permission. */
@@ -177,6 +212,41 @@ public class SupervisionService extends ISupervisionManager.Stub {
mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED);
}
+ /** Provides local services in a lazy manner. */
+ static class Injector {
+ private final Context mContext;
+ private DevicePolicyManagerInternal mDpmInternal;
+ private PackageManager mPackageManager;
+ private UserManagerInternal mUserManagerInternal;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
+ @Nullable
+ DevicePolicyManagerInternal getDpmInternal() {
+ if (mDpmInternal == null) {
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+ }
+ return mDpmInternal;
+ }
+
+ PackageManager getPackageManager() {
+ if (mPackageManager == null) {
+ mPackageManager = mContext.getPackageManager();
+ }
+ return mPackageManager;
+ }
+
+ UserManagerInternal getUserManagerInternal() {
+ if (mUserManagerInternal == null) {
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ }
+ return mUserManagerInternal;
+ }
+ }
+
+ /** Publishes local and binder services and allows the service to act during initialization. */
public static class Lifecycle extends SystemService {
private final SupervisionService mSupervisionService;
@@ -201,6 +271,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
@VisibleForTesting
+ @SuppressLint("MissingPermission") // not needed for a service
void registerProfileOwnerListener() {
IntentFilter poIntentFilter = new IntentFilter();
poIntentFilter.addAction(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
@@ -209,7 +280,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
.registerReceiverForAllUsers(
new ProfileOwnerBroadcastReceiver(),
poIntentFilter,
- /* brodcastPermission= */ null,
+ /* broadcastPermission= */ null,
/* scheduler= */ null);
}
@@ -228,19 +299,22 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
- final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
-
+ /** Implementation of the local service, API used by other services. */
private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
@Override
public boolean isActiveSupervisionApp(int uid) {
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
+ int userId = UserHandle.getUserId(uid);
+ String supervisionAppPackage = getActiveSupervisionAppPackage(userId);
+ if (supervisionAppPackage == null) {
return false;
}
- for (var packageName : packages) {
- if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
- int userId = UserHandle.getUserId(uid);
- return SupervisionService.this.isSupervisionEnabledForUser(userId);
+
+ String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
+ if (packages != null) {
+ for (var packageName : packages) {
+ if (supervisionAppPackage.equals(packageName)) {
+ return true;
+ }
}
}
return false;
@@ -253,7 +327,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
- SupervisionService.this.setSupervisionEnabledForUser(userId, enabled);
+ SupervisionService.this.setSupervisionEnabledForUser(
+ userId, enabled, getSystemSupervisionPackage());
}
@Override
@@ -274,6 +349,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
+ /** Deletes user data when the user gets removed. */
private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
@Override
public void onUserRemoved(UserInfo user) {
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
index 2adaae3943f1..976642bd563d 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -32,16 +32,18 @@ public class SupervisionServiceShellCommand extends ShellCommand {
return handleDefaultCommands(null);
}
switch (cmd) {
- case "enable": return setEnabled(true);
- case "disable": return setEnabled(false);
- default: return handleDefaultCommands(cmd);
+ case "enable":
+ return setEnabled(true);
+ case "disable":
+ return setEnabled(false);
+ default:
+ return handleDefaultCommands(cmd);
}
}
private int setEnabled(boolean enabled) {
- final var pw = getOutPrintWriter();
final var userId = UserHandle.parseUserArg(getNextArgRequired());
- mService.setSupervisionEnabledForUser(userId, enabled);
+ mService.mInternal.setSupervisionEnabledForUser(userId, enabled);
return 0;
}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
index 1dd48f581bf4..06acb91509a1 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -26,6 +26,7 @@ import android.util.IndentingPrintWriter;
public class SupervisionUserData {
public final @UserIdInt int userId;
public boolean supervisionEnabled;
+ @Nullable public String supervisionAppPackage;
public boolean supervisionLockScreenEnabled;
@Nullable public PersistableBundle supervisionLockScreenOptions;
@@ -38,6 +39,7 @@ public class SupervisionUserData {
pw.println("User " + userId + ":");
pw.increaseIndent();
pw.println("supervisionEnabled: " + supervisionEnabled);
+ pw.println("supervisionAppPackage: " + supervisionAppPackage);
pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
pw.decreaseIndent();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
index 01061f16c279..d9224eaf66ca 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
@@ -29,6 +29,7 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
private val TEST_PLUGIN_TYPE = PluginType(Int::class.java, "test_type")
+private val DISPLAY_ID = "display_id"
@SmallTest
class PluginManagerTest {
@@ -62,18 +63,18 @@ class PluginManagerTest {
fun testSubscribe() {
val pluginManager = createPluginManager()
- pluginManager.subscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.subscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
@Test
fun testUnsubscribe() {
val pluginManager = createPluginManager()
- pluginManager.unsubscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.unsubscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
private fun createPluginManager(enabled: Boolean = true): PluginManager {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
index 218e34134e93..8eb3e9fbf9b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
@@ -23,6 +23,8 @@ import org.junit.Test
private val TEST_PLUGIN_TYPE1 = PluginType(String::class.java, "test_type1")
private val TEST_PLUGIN_TYPE2 = PluginType(String::class.java, "test_type2")
+private val DISPLAY_ID_1 = "display_1"
+private val DISPLAY_ID_2 = "display_2"
@SmallTest
class PluginStorageTest {
@@ -33,9 +35,9 @@ class PluginStorageTest {
fun testUpdateValue() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -44,9 +46,9 @@ class PluginStorageTest {
fun testAddListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -55,10 +57,10 @@ class PluginStorageTest {
fun testRemoveListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
- storage.removeListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
+ storage.removeListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isNull()
}
@@ -68,10 +70,10 @@ class PluginStorageTest {
val type1Value = "value1"
val type2Value = "value2"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
- storage.updateValue(TEST_PLUGIN_TYPE2, type2Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, type2Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -81,15 +83,62 @@ class PluginStorageTest {
val type1Value = "value1"
val testChangeListener1 = TestPluginChangeListener<String>()
val testChangeListener2 = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener1)
- storage.addListener(TEST_PLUGIN_TYPE2, testChangeListener2)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, testChangeListener2)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
assertThat(testChangeListener2.receivedValue).isNull()
}
+ @Test
+ fun testUpdateGlobal_noDisplaySpecificValue() {
+ val type1Value = "value1"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1Value)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1Value)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValue() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValueRemoved() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, null)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1GlobalValue)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
private class TestPluginChangeListener<T> : PluginChangeListener<T> {
var receivedValue: T? = null
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 89b48bad2358..d6349fc0651f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,6 +16,9 @@
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.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.Process.INVALID_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,9 +30,12 @@ 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 com.android.window.flags.Flags.balClearAllowlistDuration;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -39,9 +45,11 @@ 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;
@@ -179,6 +187,41 @@ 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);
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ allowlistDurationLocked.type);
+ pir.clearAllowBgActivityStarts(token);
+ PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
+ pir.getAllowlistDurationLocked(token);
+ assertNotNull(allowlistDurationLockedAfterClear);
+ assertEquals(1000, allowlistDurationLockedAfterClear.duration);
+ assertEquals(balClearAllowlistDuration()
+ ? TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED
+ : TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ allowlistDurationLocked.type);
+ }
+
private void assertCancelReason(int expectedReason, int actualReason) {
final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
+ "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index d427c9d9ee37..e94ef5bb4871 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -39,6 +39,8 @@ import android.os.Handler;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseLongArray;
@@ -49,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
+import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.processor.MultiStatePowerAttributor;
import org.junit.Before;
@@ -59,6 +62,7 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -68,11 +72,14 @@ public class BatteryUsageStatsProviderTest {
.setProvideMainThread(true)
.build();
+ @Rule(order = 1)
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
private static final long MINUTE_IN_MS = 60 * 1000;
private static final double PRECISION = 0.00001;
- @Rule(order = 1)
+ @Rule(order = 2)
public final BatteryUsageStatsRule mStatsRule =
new BatteryUsageStatsRule(12345)
.createTempDirectory()
@@ -868,4 +875,62 @@ public class BatteryUsageStatsProviderTest {
stats.close();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED)
+ public void testIncludeSubsetOfHistory() throws IOException {
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.getHistory().setMaxHistoryBufferSize(100);
+ synchronized (batteryStats) {
+ batteryStats.setRecordAllHistoryLocked(true);
+ }
+ batteryStats.forceRecordAllHistory();
+ batteryStats.setNoAutoReset(true);
+
+ long lastIncludedEventTimestamp = 0;
+ String tag = "work work work work work work work work work work work work work work work";
+ for (int i = 1; i < 50; i++) {
+ mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(9));
+ synchronized (batteryStats) {
+ batteryStats.noteJobStartLocked(tag, 42);
+ }
+ mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(1));
+ synchronized (batteryStats) {
+ batteryStats.noteJobFinishLocked(tag, 42, 0);
+ }
+ lastIncludedEventTimestamp = mMonotonicClock.monotonicTime();
+ }
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+ mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
+
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .includeBatteryHistory()
+ .setPreferredHistoryDurationMs(TimeUnit.MINUTES.toMillis(20))
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel, 0);
+ stats.close();
+
+ parcel.setDataPosition(0);
+ BatteryUsageStats actual = BatteryUsageStats.CREATOR.createFromParcel(parcel);
+
+ long firstIncludedEventTimestamp = 0;
+ try (BatteryStatsHistoryIterator it = actual.iterateBatteryStatsHistory()) {
+ BatteryStats.HistoryItem item;
+ while ((item = it.next()) != null) {
+ if (item.eventCode == BatteryStats.HistoryItem.EVENT_JOB_START) {
+ firstIncludedEventTimestamp = item.time;
+ break;
+ }
+ }
+ }
+ actual.close();
+
+ assertThat(firstIncludedEventTimestamp)
+ .isAtLeast(lastIncludedEventTimestamp - TimeUnit.MINUTES.toMillis(30));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 4ef602f1a64c..3511ae12497a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -58,9 +58,9 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
@@ -173,6 +173,8 @@ public class MagnificationControllerTest {
@Mock
private Scroller mMockScroller;
+ private TestLooper mTestLooper;
+
// To mock package-private class
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -199,14 +201,16 @@ public class MagnificationControllerTest {
mMockResolver = new MockContentResolver();
mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- Looper looper = InstrumentationRegistry.getContext().getMainLooper();
- // Pretending ID of the Thread associated with looper as main thread ID in controller
- when(mContext.getMainLooper()).thenReturn(looper);
+ mTestLooper = new TestLooper();
+ when(mContext.getMainLooper()).thenReturn(
+ InstrumentationRegistry.getContext().getMainLooper());
when(mContext.getContentResolver()).thenReturn(mMockResolver);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
+ Settings.Secure.putFloatForUser(mMockResolver, Settings.Secure.KEY_REPEAT_ENABLED, 1,
+ CURRENT_USER_ID);
mScaleProvider = spy(new MagnificationScaleProvider(mContext));
when(mControllerCtx.getContext()).thenReturn(mContext);
@@ -251,7 +255,7 @@ public class MagnificationControllerTest {
mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
- ConcurrentUtils.DIRECT_EXECUTOR));
+ ConcurrentUtils.DIRECT_EXECUTOR, mTestLooper.getLooper()));
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -261,6 +265,7 @@ public class MagnificationControllerTest {
@After
public void tearDown() {
+ mTestLooper.dispatchAll();
FakeSettingsProvider.clearSettingsProvider();
}
@@ -880,6 +885,69 @@ public class MagnificationControllerTest {
}
@Test
+ public void magnificationCallbacks_panMagnificationContinuous() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mScreenMagnificationController);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 27 * metrics.density;
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ // Start moving right using keyboard callbacks.
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Wait for the initial delay to occur.
+ advanceTime(MagnificationController.INITIAL_KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ // It should have moved again after the handler was triggered.
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Wait for repeat delay to occur.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ // It should have moved a third time.
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Stop magnification pan.
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+
+ // It should not move again, even after the appropriate delay.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(newCenterX).isEqualTo(currentCenterX);
+ expect.that(newCenterY).isEqualTo(currentCenterY);
+ }
+
+ @Test
public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -1196,7 +1264,8 @@ public class MagnificationControllerTest {
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, lastActivatedMode);
}
- @Test public void activateFullScreenMagnification_triggerCallback() throws RemoteException {
+ @Test
+ public void activateFullScreenMagnification_triggerCallback() throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
verify(mMagnificationController).onFullScreenMagnificationActivationState(
eq(TEST_DISPLAY), eq(true));
@@ -1573,8 +1642,8 @@ public class MagnificationControllerTest {
float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- // Move right.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ // Move right using keyboard callbacks.
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_RIGHT);
float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1582,11 +1651,13 @@ public class MagnificationControllerTest {
expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
expect.that(currentCenterY).isEqualTo(newCenterY);
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
currentCenterX = newCenterX;
currentCenterY = newCenterY;
// Move left.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_LEFT);
newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1594,11 +1665,13 @@ public class MagnificationControllerTest {
expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep);
expect.that(currentCenterY).isEqualTo(newCenterY);
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
currentCenterX = newCenterX;
currentCenterY = newCenterY;
// Move down.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_DOWN);
newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
@@ -1606,17 +1679,22 @@ public class MagnificationControllerTest {
expect.that(currentCenterY).isLessThan(newCenterY);
expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep);
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
currentCenterX = newCenterX;
currentCenterY = newCenterY;
// Move up.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_UP);
newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
expect.that(currentCenterX).isEqualTo(newCenterX);
expect.that(currentCenterY).isGreaterThan(newCenterY);
expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep);
+
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
}
private void testWindowMagnificationPanWithStepSize(float expectedStepDip)
@@ -1626,28 +1704,41 @@ public class MagnificationControllerTest {
final float expectedStep = expectedStepDip * metrics.density;
// Move right.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_RIGHT);
verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f));
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
// Move left.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_LEFT);
verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f));
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
// Move down.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_DOWN);
verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
// Move up.
- mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
MagnificationController.PAN_DIRECTION_UP);
verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
+ }
+
+ private void advanceTime(long timeMs) {
+ mTestLooper.moveTimeForward(timeMs);
+ mTestLooper.dispatchAll();
}
private static class WindowMagnificationMgrCallbackDelegate implements
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 3ced56a04138..a58a9cd2a28f 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -34,7 +34,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -53,15 +52,11 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
-import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -76,11 +71,9 @@ import android.media.projection.StopReason;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -99,7 +92,6 @@ import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -110,7 +102,6 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -292,8 +283,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(stoppedCallback2).isFalse();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked() throws Exception {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -308,8 +297,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_packageAllowlisted()
throws NameNotFoundException {
@@ -325,8 +312,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
throws NameNotFoundException {
@@ -347,50 +332,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
- @Test
- public void testCreateProjection_keyguardLocked_RoleHeld() {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
- doReturn(mAppInfo)
- .when(mPackageManager)
- .getApplicationInfoAsUser(
- anyString(),
- any(ApplicationInfoFlags.class),
- any(UserHandle.class));
- MediaProjectionManagerService.MediaProjection projection =
- mService.createProjectionInternal(
- Process.myUid(),
- mContext.getPackageName(),
- TYPE_MIRRORING,
- /* isPermanentGrant= */ false,
- UserHandle.CURRENT,
- DEFAULT_DISPLAY);
- doReturn(true).when(mKeyguardManager).isKeyguardLocked();
- doReturn(PackageManager.PERMISSION_DENIED)
- .when(mPackageManager)
- .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
-
- projection.start(mIMediaProjectionCallback);
- projection.notifyVirtualDisplayCreated(10);
-
- // The projection was started because it was allowed to capture the
- // keyguard.
- assertWithMessage("Failed to run projection")
- .that(mService.getActiveProjectionInfo())
- .isNotNull();
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
- }
-
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled()
throws NameNotFoundException {
@@ -416,8 +357,6 @@ public class MediaProjectionManagerServiceTest {
}
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_noDisplayCreated()
throws NameNotFoundException {
@@ -509,8 +448,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(secondProjection).isNotEqualTo(projection);
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardNotLocked_startConsentDialog()
throws NameNotFoundException {
@@ -527,8 +464,6 @@ public class MediaProjectionManagerServiceTest {
verify(mContext).startActivityAsUser(any(), any());
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -1302,48 +1237,6 @@ public class MediaProjectionManagerServiceTest {
return mService.getProjectionInternal(UID, PACKAGE_NAME);
}
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {});
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
-
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
CountDownLatch mLatch = new CountDownLatch(1);
@Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
index 379079a0018c..10ac0495d69a 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -22,7 +22,6 @@ import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -37,13 +36,10 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -69,7 +65,6 @@ import com.android.server.SystemConfig;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -79,9 +74,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -123,6 +116,8 @@ public class MediaProjectionStopControllerTest {
private KeyguardManager mKeyguardManager;
@Mock
private TelecomManager mTelecomManager;
+ @Mock
+ private MediaProjectionStopController.RoleHolderProvider mRoleManager;
private AppOpsManager mAppOpsManager;
@Mock
@@ -145,7 +140,7 @@ public class MediaProjectionStopControllerTest {
mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
mContext.setMockPackageManager(mPackageManager);
- mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
+ mStopController = new MediaProjectionStopController(mContext, mStopConsumer, mRoleManager);
mService = new MediaProjectionManagerService(mContext,
mMediaProjectionMetricsLoggerInjector);
@@ -170,8 +165,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionNotRestricted() throws Exception {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -180,8 +173,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionRestricted() throws Exception {
MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
mediaProjection.notifyVirtualDisplayCreated(1);
@@ -239,21 +230,13 @@ public class MediaProjectionStopControllerTest {
@Test
public void testExemptFromStoppingHasAppStreamingRole() throws Exception {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- MediaProjectionManagerService.MediaProjection mediaProjection =
- createMediaProjection();
- doReturn(PackageManager.PERMISSION_DENIED).when(
- mPackageManager).checkPermission(
- RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
- assertThat(mStopController.isExemptFromStopping(mediaProjection,
- MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ doReturn(List.of(mediaProjection.packageName)).when(mRoleManager).getRoleHoldersAsUser(
+ eq(AssociationRequest.DEVICE_PROFILE_APP_STREAMING), any(UserHandle.class));
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
}
@Test
@@ -316,8 +299,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_unlocked() {
mStopController.onKeyguardLockedStateChanged(false);
@@ -325,8 +306,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_locked() {
mStopController.onKeyguardLockedStateChanged(true);
@@ -438,47 +417,4 @@ public class MediaProjectionStopControllerTest {
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, mContext.getUser(),
INVALID_DISPLAY);
}
-
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {
- });
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 5862ac65eba9..af50effb7c8e 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -92,6 +92,21 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ simulateUserStarting(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -103,6 +118,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID, preCreated = true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -114,6 +130,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -125,6 +142,21 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun profileOwnerChanged_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ broadcastProfileOwnerChanged(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -136,13 +168,14 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
fun isActiveSupervisionApp_supervisionUid_supervisionEnabled_returnsTrue() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isTrue()
}
@@ -151,7 +184,7 @@ class SupervisionServiceTest {
fun isActiveSupervisionApp_supervisionUid_supervisionNotEnabled_returnsFalse() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
}
@@ -167,15 +200,15 @@ class SupervisionServiceTest {
fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
@Test
- fun supervisionEnabledForUser_internal() {
+ fun setSupervisionEnabledForUser_internal() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
@@ -205,6 +238,13 @@ class SupervisionServiceTest {
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
+ private val supervisionProfileOwnerComponent: ComponentName
+ get() =
+ context
+ .getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent)
+ .let(ComponentName::unflattenFromString)!!
+
private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) {
val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
userInfo.preCreated = preCreated
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index a63a38da3740..0eb20eb22380 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -20,6 +20,7 @@ android_test {
],
static_libs: [
+ "compatibility-device-util-axt-minus-dexmaker",
"frameworks-base-testutils",
"services.accessibility",
"services.core",
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 4315254f68a9..69f17757b367 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -45,6 +45,8 @@
<provider android:name=".DummyProvider"
android:authorities="com.android.services.uitests" />
+ <activity android:name="android.app.ExampleActivity" />
+
</application>
<instrumentation
diff --git a/services/tests/uiservicestests/src/android/app/ExampleActivity.java b/services/tests/uiservicestests/src/android/app/ExampleActivity.java
new file mode 100644
index 000000000000..58395e4d75e1
--- /dev/null
+++ b/services/tests/uiservicestests/src/android/app/ExampleActivity.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+public class ExampleActivity extends Activity {
+}
diff --git a/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
new file mode 100644
index 000000000000..779fa1aa2f72
--- /dev/null
+++ b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationSystemUtil.runAsSystemUi;
+import static android.app.NotificationSystemUtil.toggleNotificationPolicyAccess;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.service.notification.Condition;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationManagerZenTest {
+
+ private Context mContext;
+ private NotificationManager mNotificationManager;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+
+ toggleNotificationPolicyAccess(mContext, mContext.getPackageName(), true);
+ runAsSystemUi(() -> mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+ removeAutomaticZenRules();
+ }
+
+ @After
+ public void tearDown() {
+ runAsSystemUi(() -> mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+ removeAutomaticZenRules();
+ }
+
+ private void removeAutomaticZenRules() {
+ // Delete AZRs created by this test (query "as app", then delete "as system" so they are
+ // not preserved to be restored later).
+ Map<String, AutomaticZenRule> rules = mNotificationManager.getAutomaticZenRules();
+ runAsSystemUi(() -> {
+ for (String ruleId : rules.keySet()) {
+ mNotificationManager.removeAutomaticZenRule(ruleId);
+ }
+ });
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivation() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+ STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_FALSE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+ Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+ STATE_FALSE);
+
+ // User manually activates -> it's active.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // User manually deactivates -> it's inactive.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // And app can activate and deactivate.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivation() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+ STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_FALSE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+ Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+ STATE_FALSE);
+
+ // App activates rule.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // User manually deactivates -> it's inactive.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // User manually reactivates -> it's active.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // That manual activation removed the override-deactivate, but didn't put an
+ // override-activate, so app can deactivate when its natural schedule ends.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_respectsManuallyActivated() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-on",
+ STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+ Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+ STATE_FALSE);
+
+ // App thinks rule should be inactive.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // Manually activate -> it's active.
+ runAsSystemUi(() -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // App says it should be inactive, but it's ignored.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // App says it should be active. No change now...
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // ... but when the app wants to deactivate next time, it works.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_respectsManuallyDeactivated() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_FALSE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+ Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+ STATE_FALSE);
+
+ // App activates rule.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // User manually deactivates -> it's inactive.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // App says it should be active, but it's ignored.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // App says it should be inactive. No change now...
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // ... but when the app wants to activate next time, it works.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivationFromApp() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_FALSE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+ Condition autoDeactivate = new Condition(ruleToCreate.getConditionId(), "auto-off",
+ STATE_FALSE);
+
+ // App activates rule.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // User manually deactivates from SysUI -> it's inactive.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // User manually activates from App -> it's active.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // And app can automatically deactivate it later.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivationFromApp() {
+ AutomaticZenRule ruleToCreate = createZenRule("rule");
+ String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
+ Condition manualActivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_TRUE, Condition.SOURCE_USER_ACTION);
+ Condition manualDeactivate = new Condition(ruleToCreate.getConditionId(), "manual-off",
+ STATE_FALSE, Condition.SOURCE_USER_ACTION);
+ Condition autoActivate = new Condition(ruleToCreate.getConditionId(), "auto-on",
+ STATE_TRUE);
+
+ // User manually activates from SysUI -> it's active.
+ runAsSystemUi(
+ () -> mNotificationManager.setAutomaticZenRuleState(ruleId, manualActivate));
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+
+ // User manually deactivates from App -> it's inactive.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, manualDeactivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_FALSE);
+
+ // And app can automatically activate it later.
+ mNotificationManager.setAutomaticZenRuleState(ruleId, autoActivate);
+ assertThat(mNotificationManager.getAutomaticZenRuleState(ruleId)).isEqualTo(STATE_TRUE);
+ }
+
+ private AutomaticZenRule createZenRule(String name) {
+ return createZenRule(name, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ }
+
+ private AutomaticZenRule createZenRule(String name, int filter) {
+ return new AutomaticZenRule(name, null,
+ new ComponentName(mContext, ExampleActivity.class),
+ new Uri.Builder().scheme("scheme")
+ .appendPath("path")
+ .appendQueryParameter("fake_rule", "fake_value")
+ .build(), null, filter, true);
+ }
+}
diff --git a/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java b/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java
new file mode 100644
index 000000000000..cf6e39b962ab
--- /dev/null
+++ b/services/tests/uiservicestests/src/android/app/NotificationSystemUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AmUtils;
+import com.android.compatibility.common.util.FileUtils;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class NotificationSystemUtil {
+
+ /**
+ * Runs a {@link ThrowingRunnable} as the Shell, while adopting SystemUI's permission (as
+ * checked by {@code NotificationManagerService#isCallerSystemOrSystemUi}).
+ */
+ protected static void runAsSystemUi(@NonNull ThrowingRunnable runnable) {
+ SystemUtil.runWithShellPermissionIdentity(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ runnable, Manifest.permission.STATUS_BAR_SERVICE);
+ }
+
+ static void toggleNotificationPolicyAccess(Context context, String packageName,
+ boolean on) throws IOException {
+
+ String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName
+ + " " + context.getUserId();
+
+ runCommand(command, InstrumentationRegistry.getInstrumentation());
+ AmUtils.waitForBroadcastBarrier();
+
+ NotificationManager nm = context.getSystemService(NotificationManager.class);
+ assertEquals("Notification Policy Access Grant is "
+ + nm.isNotificationPolicyAccessGranted() + " not " + on + " for "
+ + packageName, on, nm.isNotificationPolicyAccessGranted());
+ }
+
+ private static void runCommand(String command, Instrumentation instrumentation)
+ throws IOException {
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(
+ uiAutomation.executeShellCommand(command))) {
+ FileUtils.readInputStreamFully(fis);
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 704b580a80b0..832ca51ae580 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -260,7 +260,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
return FlagsParameterization.allCombinationsOf(
android.app.Flags.FLAG_API_RICH_ONGOING,
FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI,
- FLAG_MODES_UI);
+ FLAG_MODES_UI, android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS);
}
public PreferencesHelperTest(FlagsParameterization flags) {
@@ -3381,13 +3381,12 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
assertEquals(1,
- mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
- .getList().size());
+ mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, user0Uids[i]).size());
}
// user 1 records are gone
for (int i = 0; i < user1Uids.length; i++) {
- assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true)
- .getList().size());
+ assertEquals(0,
+ mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, user1Uids[i]).size());
}
}
@@ -3402,8 +3401,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1},
new int[]{UID_N_MR1}));
- assertEquals(0, mHelper.getNotificationChannels(
- PKG_N_MR1, UID_N_MR1, true, true).getList().size());
+ assertEquals(0, mHelper.getRemovedPkgNotificationChannels(PKG_N_MR1, UID_N_MR1).size());
// Not deleted
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
@@ -3472,7 +3470,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
- assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size());
+ assertEquals(0, mHelper.getRemovedPkgNotificationChannels(PKG_O, UID_O).size());
assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size());
NotificationChannel channel = getChannel();
@@ -6836,38 +6834,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
- public void testGetNotificationChannels_createIfNeeded() {
- // Test setup hasn't created any channels or read package preferences yet.
- // If we ask for notification channels _without_ creating, we should get no result.
- ParceledListSlice<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1,
- UID_N_MR1, false, false, /* createPrefsIfNeeded= */ false);
- assertThat(channels.getList().size()).isEqualTo(0);
-
- // If we ask it to create package preferences, we expect the default channel to be created
- // for N_MR1.
- channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
- false, /* createPrefsIfNeeded= */ true);
- assertThat(channels.getList().size()).isEqualTo(1);
- assertThat(channels.getList().getFirst().getId()).isEqualTo(
- NotificationChannel.DEFAULT_CHANNEL_ID);
- }
-
- @Test
@DisableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
public void testGetNotificationChannels_neverCreatesWhenFlagOff() {
- ParceledListSlice<NotificationChannel> channels;
- try {
- channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
- false, /* createPrefsIfNeeded= */ true);
- } catch (Exception e) {
- // Slog.wtf kicks in, presumably
- } finally {
- channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
- false, /* createPrefsIfNeeded= */ false);
- assertThat(channels.getList().size()).isEqualTo(0);
- }
-
+ ParceledListSlice<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1,
+ UID_N_MR1, false, false);
+ assertThat(channels.getList().size()).isEqualTo(0);
}
// Test version of PreferencesHelper whose only functional difference is that it does not
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 51706d72cb35..902a58379ae0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -589,8 +589,7 @@ public class BackgroundActivityStartControllerTests {
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_BAL; "
+ "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
- + "balRequireOptInByPendingIntentCreator: true; "
- + "balDontBringExistingBackgroundTaskStackToFg: true]");
+ + "balRequireOptInByPendingIntentCreator: true]");
}
@Test
@@ -692,7 +691,6 @@ public class BackgroundActivityStartControllerTests {
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_FGS; "
+ "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
- + "balRequireOptInByPendingIntentCreator: true; "
- + "balDontBringExistingBackgroundTaskStackToFg: true]");
+ + "balRequireOptInByPendingIntentCreator: true]");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 5ac3e483231c..7af4ede05363 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4461,7 +4461,46 @@ public class SizeCompatTests extends WindowTestsBase {
// are aligned to the top of the parentAppBounds
assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds);
assertEquals(new Rect(0, 0, 1000, 1200), bounds);
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
+ public void testInFreeform_boundsSandboxedToAppBounds() {
+ final int dw = 2800;
+ final int dh = 1400;
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ Rect appBounds = new Rect(0, 0, 1000, 500);
+ Rect bounds = new Rect(0, 0, 1000, 600);
+ mTask.getWindowConfiguration().setAppBounds(appBounds);
+ mTask.getWindowConfiguration().setBounds(bounds);
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+ // Bounds are sandboxed to appBounds in freeform.
+ assertDownScaled();
+ assertEquals(mActivity.getWindowConfiguration().getAppBounds(),
+ mActivity.getWindowConfiguration().getBounds());
+
+ // Exit freeform.
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mTask.getWindowConfiguration().setBounds(new Rect(0, 0, dw, dh));
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ assertFitted();
+ appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ bounds = mActivity.getWindowConfiguration().getBounds();
+ // Bounds are not sandboxed to appBounds.
+ assertNotEquals(appBounds, bounds);
+ assertEquals(notchHeight, appBounds.top - bounds.top);
}
@Test
diff --git a/telephony/java/com/android/internal/telephony/util/WorkerThread.java b/telephony/java/com/android/internal/telephony/util/WorkerThread.java
new file mode 100644
index 000000000000..f5b653656352
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/WorkerThread.java
@@ -0,0 +1,130 @@
+/*
+ * 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.internal.telephony.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Shared singleton worker thread for each process.
+ *
+ * This thread should be used for work that needs to be executed at standard priority
+ * but not on the main thread. This is suitable for handling asynchronous tasks that
+ * are ephemeral or require enough work that they shouldn't block the main thread, but
+ * should not block each other for more than around 100ms.
+ */
+public final class WorkerThread extends HandlerThread {
+ private static volatile WorkerThread sInstance;
+ private static volatile Handler sHandler;
+ private static volatile HandlerExecutor sHandlerExecutor;
+ private static final Object sLock = new Object();
+
+ private CountDownLatch mInitLock = new CountDownLatch(1);
+
+
+ private WorkerThread() {
+ super("android.telephony.worker");
+ }
+
+ private static void ensureThread() {
+ if (sInstance != null) return;
+ synchronized (sLock) {
+ if (sInstance != null) return;
+
+ final WorkerThread tmpThread = new WorkerThread();
+ tmpThread.start();
+
+ try {
+ tmpThread.mInitLock.await();
+ } catch (InterruptedException ignored) {
+ }
+
+
+ sHandler = new Handler(
+ tmpThread.getLooper(),
+ /* callback= */ null,
+ /* async= */ false,
+ /* shared= */ true);
+ sHandlerExecutor = new HandlerExecutor(sHandler);
+ sInstance = tmpThread; // Note: order matters here. sInstance must be assigned last.
+
+ }
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mInitLock.countDown();
+ }
+
+ /**
+ * Get the worker thread directly.
+ *
+ * Users of this thread should take care not to block it for extended periods of
+ * time.
+ *
+ * @return a HandlerThread, never null
+ */
+ @NonNull public static HandlerThread get() {
+ ensureThread();
+ return sInstance;
+ }
+
+ /**
+ * Get a Handler that can process Runnables.
+ *
+ * @return a Handler, never null
+ */
+ @NonNull public static Handler getHandler() {
+ ensureThread();
+ return sHandler;
+ }
+
+ /**
+ * Get an Executor that can process Runnables
+ *
+ * @return an Executor, never null
+ */
+ @NonNull public static Executor getExecutor() {
+ ensureThread();
+ return sHandlerExecutor;
+ }
+
+ /**
+ * A method to reset the WorkerThread from scratch.
+ *
+ * This method should only be used for unit testing. In production it would have
+ * catastrophic consequences. Do not ever use this outside of tests.
+ */
+ @VisibleForTesting
+ public static void reset() {
+ synchronized (sLock) {
+ if (sInstance == null) return;
+ sInstance.quitSafely();
+ sInstance = null;
+ sHandler = null;
+ sHandlerExecutor = null;
+ ensureThread();
+ }
+ }
+}
diff --git a/tests/testables/src/android/testing/OWNERS b/tests/testables/src/android/testing/OWNERS
new file mode 100644
index 000000000000..f31666b43654
--- /dev/null
+++ b/tests/testables/src/android/testing/OWNERS
@@ -0,0 +1,2 @@
+# MessageQueue-related classes
+per-file TestableLooper.java = mfasheh@google.com, shayba@google.com
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index be5c84c0353c..7d07d42b8042 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -16,11 +16,13 @@ package android.testing;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Instrumentation;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
+import android.os.SystemClock;
import android.os.TestLooperManager;
import android.util.ArrayMap;
@@ -32,7 +34,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.reflect.Field;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -42,6 +44,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
* and provide an easy annotation for use with tests.
*
* @see TestableLooperTest TestableLooperTest for examples.
+ *
+ * @deprecated Use {@link android.os.TestLooperManager} or {@link
+ * org.robolectric.shadows.ShadowLooper} instead.
+ * This class is not actively maintained.
+ * Both of the recommended alternatives allow fine control of execution.
+ * The Robolectric class also allows advancing time.
*/
public class TestableLooper {
@@ -50,9 +58,6 @@ public class TestableLooper {
* catch crashes.
*/
public static final boolean HOLD_MAIN_THREAD = false;
- private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
- private static final Field MESSAGE_NEXT_FIELD;
- private static final Field MESSAGE_WHEN_FIELD;
private Looper mLooper;
private MessageQueue mQueue;
@@ -61,19 +66,6 @@ public class TestableLooper {
private Handler mHandler;
private TestLooperManager mQueueWrapper;
- static {
- try {
- MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
- MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
- MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
- MESSAGE_NEXT_FIELD.setAccessible(true);
- MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
- MESSAGE_WHEN_FIELD.setAccessible(true);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException("Failed to initialize TestableLooper", e);
- }
- }
-
public TestableLooper(Looper l) throws Exception {
this(acquireLooperManager(l), l);
}
@@ -216,29 +208,17 @@ public class TestableLooper {
}
public void moveTimeForward(long milliSeconds) {
- try {
- Message msg = getMessageLinkedList();
- while (msg != null) {
- long updatedWhen = msg.getWhen() - milliSeconds;
- if (updatedWhen < 0) {
- updatedWhen = 0;
- }
- MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
- msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ long futureWhen = SystemClock.uptimeMillis() + milliSeconds;
+ // Find messages in the queue enqueued within the future time, and execute them now.
+ while (true) {
+ Long peekWhen = mQueueWrapper.peekWhen();
+ if (peekWhen == null || peekWhen > futureWhen) {
+ break;
+ }
+ Message message = mQueueWrapper.poll();
+ if (message != null) {
+ mQueueWrapper.execute(message);
}
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Access failed in TestableLooper: set - Message.when", e);
- }
- }
-
- private Message getMessageLinkedList() {
- try {
- MessageQueue queue = mLooper.getQueue();
- return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(
- "Access failed in TestableLooper: get - MessageQueue.mMessages",
- e);
}
}
diff --git a/tests/utils/testutils/java/android/os/test/OWNERS b/tests/utils/testutils/java/android/os/test/OWNERS
index 3a9129e1bb69..6448261102fa 100644
--- a/tests/utils/testutils/java/android/os/test/OWNERS
+++ b/tests/utils/testutils/java/android/os/test/OWNERS
@@ -1 +1,4 @@
per-file FakePermissionEnforcer.java = file:/tests/EnforcePermission/OWNERS
+
+# MessageQueue-related classes
+per-file TestLooper.java = mfasheh@google.com, shayba@google.com
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 56b0a25ed2dd..bca95917b9af 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -24,31 +24,38 @@ import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;
+import android.os.TestLooperManager;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.util.ArrayDeque;
+import java.util.Queue;
import java.util.concurrent.Executor;
/**
- * Creates a looper whose message queue can be manipulated
- * This allows testing code that uses a looper to dispatch messages in a deterministic manner
- * Creating a TestLooper will also install it as the looper for the current thread
+ * Creates a looper whose message queue can be manipulated This allows testing code that uses a
+ * looper to dispatch messages in a deterministic manner Creating a TestLooper will also install it
+ * as the looper for the current thread
+ *
+ * @deprecated Use {@link android.os.TestLooperManager} or {@link
+ * org.robolectric.shadows.ShadowLooper} instead.
+ * This class is not actively maintained.
+ * Both of the recommended alternatives allow fine control of execution.
+ * The Robolectric class also allows advancing time.
*/
public class TestLooper {
- protected final Looper mLooper;
+ private final Looper mLooper;
+ private final TestLooperManager mTestLooperManager;
+ private final Clock mClock;
private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
private static final Field THREAD_LOCAL_LOOPER_FIELD;
- private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
- private static final Field MESSAGE_NEXT_FIELD;
- private static final Field MESSAGE_WHEN_FIELD;
- private static final Method MESSAGE_MARK_IN_USE_METHOD;
private static final String TAG = "TestLooper";
- private final Clock mClock;
private AutoDispatchThread mAutoDispatchThread;
@@ -58,14 +65,6 @@ public class TestLooper {
LOOPER_CONSTRUCTOR.setAccessible(true);
THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
- MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
- MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
- MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
- MESSAGE_NEXT_FIELD.setAccessible(true);
- MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
- MESSAGE_WHEN_FIELD.setAccessible(true);
- MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
- MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
} catch (NoSuchFieldException | NoSuchMethodException e) {
throw new RuntimeException("Failed to initialize TestLooper", e);
}
@@ -100,6 +99,8 @@ public class TestLooper {
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
+ mTestLooperManager =
+ InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
mClock = clock;
}
@@ -111,78 +112,61 @@ public class TestLooper {
return new HandlerExecutor(new Handler(getLooper()));
}
- private Message getMessageLinkedList() {
- try {
- MessageQueue queue = mLooper.getQueue();
- return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
- e);
- }
- }
-
public void moveTimeForward(long milliSeconds) {
- try {
- Message msg = getMessageLinkedList();
- while (msg != null) {
- long updatedWhen = msg.getWhen() - milliSeconds;
- if (updatedWhen < 0) {
- updatedWhen = 0;
- }
- MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
- msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ // Drain all Messages from the queue.
+ Queue<Message> messages = new ArrayDeque<>();
+ while (true) {
+ Message message = mTestLooperManager.poll();
+ if (message == null) {
+ break;
}
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Access failed in TestLooper: set - Message.when", e);
- }
- }
- private long currentTime() {
- return mClock.uptimeMillis();
- }
+ // Adjust the Message's delivery time.
+ long newWhen = message.when - milliSeconds;
+ if (newWhen < 0) {
+ newWhen = 0;
+ }
+ message.when = newWhen;
+ messages.add(message);
+ }
- private Message messageQueueNext() {
- try {
- long now = currentTime();
-
- Message prevMsg = null;
- Message msg = getMessageLinkedList();
- if (msg != null && msg.getTarget() == null) {
- // Stalled by a barrier. Find the next asynchronous message in
- // the queue.
- do {
- prevMsg = msg;
- msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
- } while (msg != null && !msg.isAsynchronous());
+ // Repost all Messages back to the queuewith a new time.
+ while (true) {
+ Message message = messages.poll();
+ if (message == null) {
+ break;
}
- if (msg != null) {
- if (now >= msg.getWhen()) {
- // Got a message.
- if (prevMsg != null) {
- MESSAGE_NEXT_FIELD.set(prevMsg, MESSAGE_NEXT_FIELD.get(msg));
- } else {
- MESSAGE_QUEUE_MESSAGES_FIELD.set(mLooper.getQueue(),
- MESSAGE_NEXT_FIELD.get(msg));
- }
- MESSAGE_NEXT_FIELD.set(msg, null);
- MESSAGE_MARK_IN_USE_METHOD.invoke(msg);
- return msg;
- }
+
+ Runnable callback = message.getCallback();
+ Handler handler = message.getTarget();
+ long when = message.getWhen();
+
+ // The Message cannot be re-enqueued because it is marked in use.
+ // Make a copy of the Message and recycle the original.
+ // This resets {@link Message#isInUse()} but retains all other content.
+ {
+ Message newMessage = Message.obtain();
+ newMessage.copyFrom(message);
+ newMessage.setCallback(callback);
+ mTestLooperManager.recycle(message);
+ message = newMessage;
}
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new RuntimeException("Access failed in TestLooper", e);
+
+ // Send the Message back to its Handler to be re-enqueued.
+ handler.sendMessageAtTime(message, when);
}
+ }
- return null;
+ private long currentTime() {
+ return mClock.uptimeMillis();
}
/**
* @return true if there are pending messages in the message queue
*/
public synchronized boolean isIdle() {
- Message messageList = getMessageLinkedList();
-
- return messageList != null && currentTime() >= messageList.getWhen();
+ Long when = mTestLooperManager.peekWhen();
+ return when != null && currentTime() >= when;
}
/**
@@ -190,7 +174,7 @@ public class TestLooper {
*/
public synchronized Message nextMessage() {
if (isIdle()) {
- return messageQueueNext();
+ return mTestLooperManager.poll();
} else {
return null;
}
@@ -202,7 +186,7 @@ public class TestLooper {
*/
public synchronized void dispatchNext() {
assertTrue(isIdle());
- Message msg = messageQueueNext();
+ Message msg = mTestLooperManager.poll();
if (msg == null) {
return;
}