summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/bootanimation/OWNERS1
-rw-r--r--core/api/current.txt69
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java13
-rw-r--r--core/java/android/app/contextualsearch/ContextualSearchManager.java9
-rw-r--r--core/java/android/app/notification.aconfig8
-rw-r--r--core/java/android/content/pm/ActivityInfo.java14
-rw-r--r--core/java/android/net/vcn/flags.aconfig34
-rw-r--r--core/java/android/os/Build.java215
-rw-r--r--core/java/android/os/RemoteCallbackList.java470
-rw-r--r--core/java/android/view/Display.java13
-rw-r--r--core/java/android/view/DisplayInfo.java12
-rw-r--r--core/java/android/view/SurfaceControl.java7
-rw-r--r--core/java/android/view/ViewRootImpl.java41
-rw-r--r--core/java/android/widget/RemoteCollectionItemsAdapter.java10
-rw-r--r--core/java/android/widget/RemoteViews.java14
-rw-r--r--core/java/android/window/BackEvent.java16
-rw-r--r--core/java/android/window/SurfaceSyncGroup.java10
-rw-r--r--core/java/android/window/TaskSnapshot.java7
-rw-r--r--core/java/android/window/WindowContainerTransaction.java54
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java4
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig11
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java2
-rw-r--r--core/java/com/android/internal/widget/NotificationRowIconView.java224
-rw-r--r--core/jni/android_media_AudioSystem.cpp373
-rw-r--r--core/jni/android_view_SurfaceControl.cpp5
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_background_color.xml23
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_text_color.xml23
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml23
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml23
-rw-r--r--core/res/res/drawable-watch-v36/btn_background_material_filled.xml28
-rw-r--r--core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml28
-rw-r--r--core/res/res/values-watch-v36/colors.xml18
-rw-r--r--core/res/res/values-watch-v36/config.xml20
-rw-r--r--core/res/res/values-watch-v36/dimens_material.xml28
-rw-r--r--core/res/res/values-watch-v36/styles_material.xml58
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java17
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsTest.java59
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java95
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java263
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java101
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java107
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java41
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java19
-rw-r--r--media/java/android/media/AudioSystem.java11
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt4
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt2
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt4
-rw-r--r--packages/SettingsProvider/Android.bp1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java30
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java72
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig6
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt20
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt106
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt62
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt18
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt28
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt28
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt173
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt41
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt48
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt65
-rw-r--r--packages/SystemUI/customization/res/values/ids.xml3
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt34
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt34
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt (renamed from packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockController.kt)36
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt261
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt314
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt64
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt5
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml4
-rw-r--r--packages/SystemUI/res/values-land/config.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/config.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt352
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt147
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt19
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt3
-rw-r--r--ravenwood/Android.bp2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java6
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java9
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java2
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java33
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java2
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig8
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java40
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java9
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java13
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java12
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java3
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessReason.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java17
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java3
-rw-r--r--services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java15
-rw-r--r--services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java16
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java3
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java12
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java25
-rw-r--r--services/core/java/com/android/server/display/mode/SyntheticModeManager.java9
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java7
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java85
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java2
-rw-r--r--services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java2
-rw-r--r--services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java80
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerNative.java13
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java61
-rw-r--r--services/core/java/com/android/server/power/Notifier.java4
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java25
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java27
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java29
-rw-r--r--services/core/java/com/android/server/wm/DeferredDisplayUpdater.java1
-rw-r--r--services/core/java/com/android/server/wm/Task.java38
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java75
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java36
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java5
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java6
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java76
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt29
-rw-r--r--services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java54
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java56
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java27
-rw-r--r--services/tests/security/forensic/OWNERS3
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java95
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java192
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbManagerInternal.java4
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java7
-rw-r--r--telecomm/java/android/telecom/Logging/Session.java240
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java126
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java5
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java2
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java57
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java14
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml6
-rw-r--r--tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java43
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java12
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java7
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java9
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp2
292 files changed, 5130 insertions, 3969 deletions
diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS
index b6fb007bea52..2eda44dabec3 100644
--- a/cmds/bootanimation/OWNERS
+++ b/cmds/bootanimation/OWNERS
@@ -1,3 +1,4 @@
dupin@google.com
shanh@google.com
jreck@google.com
+rahulbanerjee@google.com \ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index db302f43dfe0..6f60378d73e3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32880,6 +32880,41 @@ package android.os {
}
@FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+ field public static final int BASE = 100000; // 0x186a0
+ field public static final int BASE_1_1 = 200000; // 0x30d40
+ field public static final int CUPCAKE = 300000; // 0x493e0
+ field public static final int DONUT = 400000; // 0x61a80
+ field public static final int ECLAIR = 500000; // 0x7a120
+ field public static final int ECLAIR_0_1 = 600000; // 0x927c0
+ field public static final int ECLAIR_MR1 = 700000; // 0xaae60
+ field public static final int FROYO = 800000; // 0xc3500
+ field public static final int GINGERBREAD = 900000; // 0xdbba0
+ field public static final int GINGERBREAD_MR1 = 1000000; // 0xf4240
+ field public static final int HONEYCOMB = 1100000; // 0x10c8e0
+ field public static final int HONEYCOMB_MR1 = 1200000; // 0x124f80
+ field public static final int HONEYCOMB_MR2 = 1300000; // 0x13d620
+ field public static final int ICE_CREAM_SANDWICH = 1400000; // 0x155cc0
+ field public static final int ICE_CREAM_SANDWICH_MR1 = 1500000; // 0x16e360
+ field public static final int JELLY_BEAN = 1600000; // 0x186a00
+ field public static final int JELLY_BEAN_MR1 = 1700000; // 0x19f0a0
+ field public static final int JELLY_BEAN_MR2 = 1800000; // 0x1b7740
+ field public static final int KITKAT = 1900000; // 0x1cfde0
+ field public static final int KITKAT_WATCH = 2000000; // 0x1e8480
+ field public static final int LOLLIPOP = 2100000; // 0x200b20
+ field public static final int LOLLIPOP_MR1 = 2200000; // 0x2191c0
+ field public static final int M = 2300000; // 0x231860
+ field public static final int N = 2400000; // 0x249f00
+ field public static final int N_MR1 = 2500000; // 0x2625a0
+ field public static final int O = 2600000; // 0x27ac40
+ field public static final int O_MR1 = 2700000; // 0x2932e0
+ field public static final int P = 2800000; // 0x2ab980
+ field public static final int Q = 2900000; // 0x2c4020
+ field public static final int R = 3000000; // 0x2dc6c0
+ field public static final int S = 3100000; // 0x2f4d60
+ field public static final int S_V2 = 3200000; // 0x30d400
+ field public static final int TIRAMISU = 3300000; // 0x325aa0
+ field public static final int UPSIDE_DOWN_CAKE = 3400000; // 0x33e140
+ field public static final int VANILLA_ICE_CREAM = 3500000; // 0x3567e0
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -33933,12 +33968,9 @@ package android.os {
public class RemoteCallbackList<E extends android.os.IInterface> {
ctor public RemoteCallbackList();
method public int beginBroadcast();
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public void broadcast(@NonNull java.util.function.Consumer<E>);
method public void finishBroadcast();
method public Object getBroadcastCookie(int);
method public E getBroadcastItem(int);
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy();
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize();
method public Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
method public E getRegisteredCallbackItem(int);
@@ -33948,16 +33980,6 @@ package android.os {
method public boolean register(E);
method public boolean register(E, Object);
method public boolean unregister(E);
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_DROP = 3; // 0x3
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; // 0x1
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; // 0x2
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_UNSET = 0; // 0x0
- }
-
- @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
- ctor public RemoteCallbackList.Builder(int);
- method @NonNull public android.os.RemoteCallbackList<E> build();
- method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
}
public class RemoteException extends android.util.AndroidException {
@@ -43851,8 +43873,8 @@ package android.telephony {
field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
+ field public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
+ field public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
@@ -43935,7 +43957,7 @@ package android.telephony {
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
- field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
+ field public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
@@ -45973,7 +45995,7 @@ package android.telephony {
method @Nullable public String getMncString();
method @Deprecated public String getNumber();
method public int getPortIndex();
- method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
+ method @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
@@ -46056,9 +46078,9 @@ package android.telephony {
field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
- field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
- field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
- field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
+ field public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
+ field public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
+ field public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
@@ -46307,8 +46329,8 @@ package android.telephony {
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled();
- method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable();
- method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable();
+ method public boolean isDeviceSmsCapable();
+ method public boolean isDeviceVoiceCapable();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
@@ -46367,7 +46389,7 @@ package android.telephony {
field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
- field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+ field public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
@@ -50947,6 +50969,7 @@ package android.view {
method public android.view.Display.Mode[] getSupportedModes();
method @Deprecated public float[] getSupportedRefreshRates();
method @Deprecated public int getWidth();
+ method @FlaggedApi("com.android.server.display.feature.flags.enable_has_arr_support") public boolean hasArrSupport();
method public boolean isHdr();
method public boolean isHdrSdrRatioAvailable();
method public boolean isMinimalPostProcessingSupported();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cab836e147c4..fa4fc43c3418 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7319,6 +7319,7 @@ package android.media {
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1
+ field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_VOLUME_SHAPER = 32; // 0x20
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c79fc510c25a..1ff8c510b6bf 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1101,6 +1101,7 @@ package android.content.pm {
field public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L; // 0xfd27b38L
field public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L; // 0xf4156bcL
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
+ field public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // 0x15498ba7L
}
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8b33417e0a79..4ef5b5163fef 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11296,7 +11296,11 @@ public class Notification implements Parcelable
* @see Segment
*/
public @NonNull ProgressStyle setProgressSegments(@NonNull List<Segment> progressSegments) {
- mProgressSegments = new ArrayList<>(progressSegments.size());
+ if (mProgressSegments == null) {
+ mProgressSegments = new ArrayList<>();
+ }
+ mProgressSegments.clear();
+ mProgressSegments.addAll(progressSegments);
return this;
}
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 08ecced234a9..06d95f5270c3 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -137,8 +137,10 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
.TOKENIZER_TYPE_VERBATIM)
.build())
.addProperty(
- new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+ new AppSearchSchema.LongPropertyConfig.Builder(PROPERTY_ENABLED)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE)
.build())
.addProperty(
new AppSearchSchema.StringPropertyConfig.Builder(
@@ -212,19 +214,14 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
}
/**
- * Sets an indicator specifying if the function is enabled or not. This would override the
- * default enabled state in the static metadata ({@link
- * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to null
- * to clear the override.
- * TODO(369683073) Replace the tristate Boolean with IntDef EnabledState.
+ * Sets an indicator specifying the function enabled state.
*/
@NonNull
public Builder setEnabled(@EnabledState int enabledState) {
if (enabledState != APP_FUNCTION_STATE_DEFAULT
&& enabledState != APP_FUNCTION_STATE_ENABLED
&& enabledState != APP_FUNCTION_STATE_DISABLED) {
- throw new IllegalArgumentException(
- "Value of EnabledState is unsupported.");
+ throw new IllegalArgumentException("Value of EnabledState is unsupported.");
}
setPropertyLong(PROPERTY_ENABLED, enabledState);
return this;
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
index cfbe7416bf9d..3438cc861661 100644
--- a/core/java/android/app/contextualsearch/ContextualSearchManager.java
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -102,6 +102,7 @@ public final class ContextualSearchManager {
* Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
*/
public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+
/**
* Intent action for contextual search invocation. The app providing the contextual search
* experience must add this intent filter action to the activity it wants to be launched.
@@ -111,6 +112,14 @@ public final class ContextualSearchManager {
public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH =
"android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
+ /**
+ * System feature declaring that the device supports Contextual Search.
+ *
+ * @hide
+ */
+ public static final String FEATURE_CONTEXTUAL_SEARCH =
+ "com.google.android.feature.CONTEXTUAL_SEARCH";
+
/** Entrypoint to be used when a user long presses on the nav handle. */
public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1;
/** Entrypoint to be used when a user long presses on the home button. */
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 11e885055162..37fa9a26b91c 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -6,6 +6,14 @@ container: "system"
# flag relates to.
flag {
+ name: "notifications_redesign_app_icons"
+ namespace: "systemui"
+ description: "Notifications Redesign: Use app icons in notification rows (not to be confused with"
+ " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)."
+ bug: "371174789"
+}
+
+flag {
name: "modes_api"
is_exported: true
namespace: "systemui"
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 481e6b530162..ce52825ddb73 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -21,10 +21,12 @@ import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1204,6 +1206,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public @interface SizeChangesSupportMode {}
/**
+ * This change id makes the restriction of fixed orientation, aspect ratio, and resizability
+ * of the app to be ignored, which means making the app fill the given available area.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated public API.
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // buganizer id
+
+ /**
* This change id enables compat policy that ignores app requested orientation in
* response to an app calling {@link android.app.Activity#setRequestedOrientation}. See
* com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation for
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index dcb363ccf535..efddd1f9fc6f 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -21,38 +21,4 @@ flag{
namespace: "vcn"
description: "Feature flag for enabling network metric monitor"
bug: "282996138"
-}
-
-flag{
- name: "validate_network_on_ipsec_loss"
- namespace: "vcn"
- description: "Trigger network validation when IPsec packet loss exceeds the threshold"
- bug: "329139898"
-}
-
-flag{
- name: "evaluate_ipsec_loss_on_lp_nc_change"
- namespace: "vcn"
- description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change"
- bug: "323238888"
-}
-
-flag{
- name: "enforce_main_user"
- namespace: "vcn"
- description: "Enforce main user to make VCN HSUM compatible"
- bug: "310310661"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag{
- name: "handle_seq_num_leap"
- namespace: "vcn"
- description: "Do not report bad network when there is a suspected sequence number leap"
- bug: "332598276"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
} \ No newline at end of file
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index d3ba73ed1421..a89483394611 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1278,6 +1278,41 @@ public class Build {
/** @hide */
@IntDef(value = {
+ VERSION_CODES_FULL.BASE,
+ VERSION_CODES_FULL.BASE_1_1,
+ VERSION_CODES_FULL.CUPCAKE,
+ VERSION_CODES_FULL.DONUT,
+ VERSION_CODES_FULL.ECLAIR,
+ VERSION_CODES_FULL.ECLAIR_0_1,
+ VERSION_CODES_FULL.ECLAIR_MR1,
+ VERSION_CODES_FULL.FROYO,
+ VERSION_CODES_FULL.GINGERBREAD,
+ VERSION_CODES_FULL.GINGERBREAD_MR1,
+ VERSION_CODES_FULL.HONEYCOMB,
+ VERSION_CODES_FULL.HONEYCOMB_MR1,
+ VERSION_CODES_FULL.HONEYCOMB_MR2,
+ VERSION_CODES_FULL.ICE_CREAM_SANDWICH,
+ VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1,
+ VERSION_CODES_FULL.JELLY_BEAN,
+ VERSION_CODES_FULL.JELLY_BEAN_MR1,
+ VERSION_CODES_FULL.JELLY_BEAN_MR2,
+ VERSION_CODES_FULL.KITKAT,
+ VERSION_CODES_FULL.KITKAT_WATCH,
+ VERSION_CODES_FULL.LOLLIPOP,
+ VERSION_CODES_FULL.LOLLIPOP_MR1,
+ VERSION_CODES_FULL.M,
+ VERSION_CODES_FULL.N,
+ VERSION_CODES_FULL.N_MR1,
+ VERSION_CODES_FULL.O,
+ VERSION_CODES_FULL.O_MR1,
+ VERSION_CODES_FULL.P,
+ VERSION_CODES_FULL.Q,
+ VERSION_CODES_FULL.R,
+ VERSION_CODES_FULL.S,
+ VERSION_CODES_FULL.S_V2,
+ VERSION_CODES_FULL.TIRAMISU,
+ VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
+ VERSION_CODES_FULL.VANILLA_ICE_CREAM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SdkIntFull {}
@@ -1299,6 +1334,186 @@ public class Build {
// Use the last 5 digits for the minor version. This allows the
// minor version to be set to CUR_DEVELOPMENT.
private static final int SDK_INT_MULTIPLIER = 100000;
+
+ /**
+ * Android 1.0.
+ */
+ public static final int BASE = VERSION_CODES.BASE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 2.0.
+ */
+ public static final int BASE_1_1 = VERSION_CODES.BASE_1_1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 3.0.
+ */
+ public static final int CUPCAKE = VERSION_CODES.CUPCAKE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 4.0.
+ */
+ public static final int DONUT = VERSION_CODES.DONUT * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 5.0.
+ */
+ public static final int ECLAIR = VERSION_CODES.ECLAIR * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 6.0.
+ */
+ public static final int ECLAIR_0_1 = VERSION_CODES.ECLAIR_0_1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 7.0.
+ */
+ public static final int ECLAIR_MR1 = VERSION_CODES.ECLAIR_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 8.0.
+ */
+ public static final int FROYO = VERSION_CODES.FROYO * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 9.0.
+ */
+ public static final int GINGERBREAD = VERSION_CODES.GINGERBREAD * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 10.0.
+ */
+ public static final int GINGERBREAD_MR1 =
+ VERSION_CODES.GINGERBREAD_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 11.0.
+ */
+ public static final int HONEYCOMB = VERSION_CODES.HONEYCOMB * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 12.0.
+ */
+ public static final int HONEYCOMB_MR1 = VERSION_CODES.HONEYCOMB_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 13.0.
+ */
+ public static final int HONEYCOMB_MR2 = VERSION_CODES.HONEYCOMB_MR2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 14.0.
+ */
+ public static final int ICE_CREAM_SANDWICH =
+ VERSION_CODES.ICE_CREAM_SANDWICH * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 15.0.
+ */
+ public static final int ICE_CREAM_SANDWICH_MR1 =
+ VERSION_CODES.ICE_CREAM_SANDWICH_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 16.0.
+ */
+ public static final int JELLY_BEAN = VERSION_CODES.JELLY_BEAN * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 17.0.
+ */
+ public static final int JELLY_BEAN_MR1 = VERSION_CODES.JELLY_BEAN_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 18.0.
+ */
+ public static final int JELLY_BEAN_MR2 = VERSION_CODES.JELLY_BEAN_MR2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 19.0.
+ */
+ public static final int KITKAT = VERSION_CODES.KITKAT * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 20.0.
+ */
+ public static final int KITKAT_WATCH = VERSION_CODES.KITKAT_WATCH * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 21.0.
+ */
+ public static final int LOLLIPOP = VERSION_CODES.LOLLIPOP * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 22.0.
+ */
+ public static final int LOLLIPOP_MR1 = VERSION_CODES.LOLLIPOP_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 23.0.
+ */
+ public static final int M = VERSION_CODES.M * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 24.0.
+ */
+ public static final int N = VERSION_CODES.N * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 25.0.
+ */
+ public static final int N_MR1 = VERSION_CODES.N_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 26.0.
+ */
+ public static final int O = VERSION_CODES.O * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 27.0.
+ */
+ public static final int O_MR1 = VERSION_CODES.O_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 28.0.
+ */
+ public static final int P = VERSION_CODES.P * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 29.0.
+ */
+ public static final int Q = VERSION_CODES.Q * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 30.0.
+ */
+ public static final int R = VERSION_CODES.R * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 31.0.
+ */
+ public static final int S = VERSION_CODES.S * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 32.0.
+ */
+ public static final int S_V2 = VERSION_CODES.S_V2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 33.0.
+ */
+ public static final int TIRAMISU = VERSION_CODES.TIRAMISU * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 34.0.
+ */
+ public static final int UPSIDE_DOWN_CAKE =
+ VERSION_CODES.UPSIDE_DOWN_CAKE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 35.0.
+ */
+ public static final int VANILLA_ICE_CREAM =
+ VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
}
/**
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index f82c8221e4f9..769cbdd9886d 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,18 +16,11 @@
package android.os;
-import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -37,7 +30,7 @@ import java.util.function.Consumer;
* {@link android.app.Service} to its clients. In particular, this:
*
* <ul>
- * <li> Keeps track of a set of registered {@link IInterface} objects,
+ * <li> Keeps track of a set of registered {@link IInterface} callbacks,
* taking care to identify them through their underlying unique {@link IBinder}
* (by calling {@link IInterface#asBinder IInterface.asBinder()}.
* <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
@@ -54,7 +47,7 @@ import java.util.function.Consumer;
* the registered clients, use {@link #beginBroadcast},
* {@link #getBroadcastItem}, and {@link #finishBroadcast}.
*
- * <p>If a registered interface's process goes away, this class will take
+ * <p>If a registered callback's process goes away, this class will take
* care of automatically removing it from the list. If you want to do
* additional work in this situation, you can create a subclass that
* implements the {@link #onCallbackDied} method.
@@ -63,310 +56,78 @@ import java.util.function.Consumer;
public class RemoteCallbackList<E extends IInterface> {
private static final String TAG = "RemoteCallbackList";
- private static final int DEFAULT_MAX_QUEUE_SIZE = 1000;
-
-
- /**
- * @hide
- */
- @IntDef(prefix = {"FROZEN_CALLEE_POLICY_"}, value = {
- FROZEN_CALLEE_POLICY_UNSET,
- FROZEN_CALLEE_POLICY_ENQUEUE_ALL,
- FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT,
- FROZEN_CALLEE_POLICY_DROP,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface FrozenCalleePolicy {
- }
-
- /**
- * Callbacks are invoked immediately regardless of the frozen state of the target process.
- *
- * Not recommended. Only exists for backward-compatibility. This represents the behavior up to
- * SDK 35. Starting with SDK 36, clients should set a policy to govern callback invocations when
- * recipients are frozen.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_UNSET = 0;
-
- /**
- * When the callback recipient's process is frozen, callbacks are enqueued so they're invoked
- * after the recipient is unfrozen.
- *
- * This is commonly used when the recipient wants to receive all callbacks without losing any
- * history, e.g. the recipient maintains a running count of events that occurred.
- *
- * Queued callbacks are invoked in the order they were originally broadcasted.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1;
-
- /**
- * When the callback recipient's process is frozen, only the most recent callback is enqueued,
- * which is later invoked after the recipient is unfrozen.
- *
- * This can be used when only the most recent state matters, for instance when clients are
- * listening to screen brightness changes.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2;
-
- /**
- * When the callback recipient's process is frozen, callbacks are suppressed as if they never
- * happened.
- *
- * This could be useful in the case where the recipient wishes to react to callbacks only when
- * they occur while the recipient is not frozen. For example, certain network events are only
- * worth responding to if the response can be immediate. Another example is recipients having
- * another way of getting the latest state once it's unfrozen. Therefore there is no need to
- * save callbacks that happened while the recipient was frozen.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_DROP = 3;
-
@UnsupportedAppUsage
- /*package*/ ArrayMap<IBinder, Interface> mInterfaces = new ArrayMap<IBinder, Interface>();
+ /*package*/ ArrayMap<IBinder, Callback> mCallbacks
+ = new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private StringBuilder mRecentCallers;
- private final @FrozenCalleePolicy int mFrozenCalleePolicy;
- private final int mMaxQueueSize;
-
- private final class Interface implements IBinder.DeathRecipient,
- IBinder.FrozenStateChangeCallback {
- final IBinder mBinder;
- final E mInterface;
+ private final class Callback implements IBinder.DeathRecipient {
+ final E mCallback;
final Object mCookie;
- final Queue<Consumer<E>> mCallbackQueue;
- int mCurrentState = IBinder.FrozenStateChangeCallback.STATE_UNFROZEN;
- Interface(E callbackInterface, Object cookie) {
- mBinder = callbackInterface.asBinder();
- mInterface = callbackInterface;
+ Callback(E callback, Object cookie) {
+ mCallback = callback;
mCookie = cookie;
- mCallbackQueue = mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_ALL
- || mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT
- ? new ConcurrentLinkedQueue<>() : null;
- }
-
- @Override
- public synchronized void onFrozenStateChanged(@NonNull IBinder who, int state) {
- if (state == STATE_UNFROZEN && mCallbackQueue != null) {
- while (!mCallbackQueue.isEmpty()) {
- Consumer<E> callback = mCallbackQueue.poll();
- callback.accept(mInterface);
- }
- }
- mCurrentState = state;
- }
-
- void addCallback(@NonNull Consumer<E> callback) {
- if (mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_UNSET) {
- callback.accept(mInterface);
- return;
- }
- synchronized (this) {
- if (mCurrentState == STATE_UNFROZEN) {
- callback.accept(mInterface);
- return;
- }
- switch (mFrozenCalleePolicy) {
- case FROZEN_CALLEE_POLICY_ENQUEUE_ALL:
- if (mCallbackQueue.size() >= mMaxQueueSize) {
- mCallbackQueue.poll();
- }
- mCallbackQueue.offer(callback);
- break;
- case FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT:
- mCallbackQueue.clear();
- mCallbackQueue.offer(callback);
- break;
- case FROZEN_CALLEE_POLICY_DROP:
- // Do nothing. Just ignore the callback.
- break;
- case FROZEN_CALLEE_POLICY_UNSET:
- // Do nothing. Should have returned at the start of the method.
- break;
- }
- }
- }
-
- public void maybeSubscribeToFrozenCallback() throws RemoteException {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.addFrozenStateChangeCallback(this);
- }
- }
-
- public void maybeUnsubscribeFromFrozenCallback() {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.removeFrozenStateChangeCallback(this);
- }
}
public void binderDied() {
- synchronized (mInterfaces) {
- mInterfaces.remove(mBinder);
- maybeUnsubscribeFromFrozenCallback();
- }
- onCallbackDied(mInterface, mCookie);
- }
- }
-
- /**
- * Builder for {@link RemoteCallbackList}.
- *
- * @param <E> The remote callback interface type.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final class Builder<E extends IInterface> {
- private @FrozenCalleePolicy int mFrozenCalleePolicy;
- private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
-
- /**
- * Creates a Builder for {@link RemoteCallbackList}.
- *
- * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
- * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
- * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
- * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
- */
- public Builder(@FrozenCalleePolicy int frozenCalleePolicy) {
- mFrozenCalleePolicy = frozenCalleePolicy;
- }
-
- /**
- * Sets the max queue size.
- *
- * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
- * recipient's process is frozen. Once the limit is reached, the oldest callback is dropped
- * to keep the size under the limit. Should only be called for
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- *
- * @return This builder.
- * @throws IllegalArgumentException if the maxQueueSize is not positive.
- * @throws UnsupportedOperationException if frozenCalleePolicy is not
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- */
- public @NonNull Builder setMaxQueueSize(int maxQueueSize) {
- if (maxQueueSize <= 0) {
- throw new IllegalArgumentException("maxQueueSize must be positive");
- }
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_ENQUEUE_ALL) {
- throw new UnsupportedOperationException(
- "setMaxQueueSize can only be called for FROZEN_CALLEE_POLICY_ENQUEUE_ALL");
+ synchronized (mCallbacks) {
+ mCallbacks.remove(mCallback.asBinder());
}
- mMaxQueueSize = maxQueueSize;
- return this;
- }
-
- /**
- * Builds and returns a {@link RemoteCallbackList}.
- *
- * @return The built {@link RemoteCallbackList} object.
- */
- public @NonNull RemoteCallbackList<E> build() {
- return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
+ onCallbackDied(mCallback, mCookie);
}
}
/**
- * Returns the frozen callee policy.
- *
- * @return The frozen callee policy.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public @FrozenCalleePolicy int getFrozenCalleePolicy() {
- return mFrozenCalleePolicy;
- }
-
- /**
- * Returns the max queue size.
- *
- * @return The max queue size.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public int getMaxQueueSize() {
- return mMaxQueueSize;
- }
-
- /**
- * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to
- * <pre>
- * new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
- * </pre>
- */
- public RemoteCallbackList() {
- this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE);
- }
-
- /**
- * Creates a RemoteCallbackList with the specified frozen callee policy.
- *
- * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
- * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
- * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
- * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
- *
- * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
- * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be
- * dropped to keep the size under limit. Ignored except for
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- */
- private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) {
- mFrozenCalleePolicy = frozenCalleePolicy;
- mMaxQueueSize = maxQueueSize;
- }
-
- /**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
*/
- public boolean register(E callbackInterface) {
- return register(callbackInterface, null);
+ public boolean register(E callback) {
+ return register(callback, null);
}
/**
- * Add a new interface to the list. This interface will remain in the list
+ * Add a new callback to the list. This callback will remain in the list
* until a corresponding call to {@link #unregister} or its hosting process
- * goes away. If the interface was already registered (determined by
- * checking to see if the {@link IInterface#asBinder callbackInterface.asBinder()}
- * object is already in the list), then it will be replaced with the new interface.
+ * goes away. If the callback was already registered (determined by
+ * checking to see if the {@link IInterface#asBinder callback.asBinder()}
+ * object is already in the list), then it will be replaced with the new callback.
* Registrations are not counted; a single call to {@link #unregister}
- * will remove an interface after any number calls to register it.
+ * will remove a callback after any number calls to register it.
*
- * @param callbackInterface The callback interface to be added to the list. Must
+ * @param callback The callback interface to be added to the list. Must
* not be null -- passing null here will cause a NullPointerException.
* Most services will want to check for null before calling this with
* an object given from a client, so that clients can't crash the
* service with bad data.
*
* @param cookie Optional additional data to be associated with this
- * interface.
+ * callback.
*
- * @return Returns true if the interface was successfully added to the list.
+ * @return Returns true if the callback was successfully added to the list.
* Returns false if it was not added, either because {@link #kill} had
- * previously been called or the interface's process has gone away.
+ * previously been called or the callback's process has gone away.
*
* @see #unregister
* @see #kill
* @see #onCallbackDied
*/
- public boolean register(E callbackInterface, Object cookie) {
- synchronized (mInterfaces) {
+ public boolean register(E callback, Object cookie) {
+ synchronized (mCallbacks) {
if (mKilled) {
return false;
}
// Flag unusual case that could be caused by a leak. b/36778087
- logExcessiveInterfaces();
- IBinder binder = callbackInterface.asBinder();
+ logExcessiveCallbacks();
+ IBinder binder = callback.asBinder();
try {
- Interface i = new Interface(callbackInterface, cookie);
- unregister(callbackInterface);
- binder.linkToDeath(i, 0);
- i.maybeSubscribeToFrozenCallback();
- mInterfaces.put(binder, i);
+ Callback cb = new Callback(callback, cookie);
+ unregister(callback);
+ binder.linkToDeath(cb, 0);
+ mCallbacks.put(binder, cb);
return true;
} catch (RemoteException e) {
return false;
@@ -375,28 +136,27 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Remove from the list an interface that was previously added with
+ * Remove from the list a callback that was previously added with
* {@link #register}. This uses the
- * {@link IInterface#asBinder callbackInterface.asBinder()} object to correctly
+ * {@link IInterface#asBinder callback.asBinder()} object to correctly
* find the previous registration.
* Registrations are not counted; a single unregister call will remove
- * an interface after any number calls to {@link #register} for it.
+ * a callback after any number calls to {@link #register} for it.
*
- * @param callbackInterface The interface to be removed from the list. Passing
+ * @param callback The callback to be removed from the list. Passing
* null here will cause a NullPointerException, so you will generally want
* to check for null before calling.
*
- * @return Returns true if the interface was found and unregistered. Returns
- * false if the given interface was not found on the list.
+ * @return Returns true if the callback was found and unregistered. Returns
+ * false if the given callback was not found on the list.
*
* @see #register
*/
- public boolean unregister(E callbackInterface) {
- synchronized (mInterfaces) {
- Interface i = mInterfaces.remove(callbackInterface.asBinder());
- if (i != null) {
- i.mInterface.asBinder().unlinkToDeath(i, 0);
- i.maybeUnsubscribeFromFrozenCallback();
+ public boolean unregister(E callback) {
+ synchronized (mCallbacks) {
+ Callback cb = mCallbacks.remove(callback.asBinder());
+ if (cb != null) {
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
return true;
}
return false;
@@ -404,21 +164,20 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Disable this interface list. All registered interfaces are unregistered,
+ * Disable this callback list. All registered callbacks are unregistered,
* and the list is disabled so that future calls to {@link #register} will
* fail. This should be used when a Service is stopping, to prevent clients
- * from registering interfaces after it is stopped.
+ * from registering callbacks after it is stopped.
*
* @see #register
*/
public void kill() {
- synchronized (mInterfaces) {
- for (int cbi = mInterfaces.size() - 1; cbi >= 0; cbi--) {
- Interface i = mInterfaces.valueAt(cbi);
- i.mInterface.asBinder().unlinkToDeath(i, 0);
- i.maybeUnsubscribeFromFrozenCallback();
+ synchronized (mCallbacks) {
+ for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
+ Callback cb = mCallbacks.valueAt(cbi);
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
}
- mInterfaces.clear();
+ mCallbacks.clear();
mKilled = true;
}
}
@@ -427,15 +186,15 @@ public class RemoteCallbackList<E extends IInterface> {
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
- public void onCallbackDied(E callbackInterface) {
+ public void onCallbackDied(E callback) {
}
/**
- * Called when the process hosting an interface in the list has gone away.
+ * Called when the process hosting a callback in the list has gone away.
* The default implementation calls {@link #onCallbackDied(E)}
* for backwards compatibility.
*
- * @param callbackInterface The interface whose process has died. Note that, since
+ * @param callback The callback whose process has died. Note that, since
* its process has died, you can not make any calls on to this interface.
* You can, however, retrieve its IBinder and compare it with another
* IBinder to see if it is the same object.
@@ -444,15 +203,13 @@ public class RemoteCallbackList<E extends IInterface> {
*
* @see #register
*/
- public void onCallbackDied(E callbackInterface, Object cookie) {
- onCallbackDied(callbackInterface);
+ public void onCallbackDied(E callback, Object cookie) {
+ onCallbackDied(callback);
}
/**
- * Use {@link #broadcast(Consumer)} instead to ensure proper handling of frozen processes.
- *
- * Prepare to start making calls to the currently registered interfaces.
- * This creates a copy of the interface list, which you can retrieve items
+ * Prepare to start making calls to the currently registered callbacks.
+ * This creates a copy of the callback list, which you can retrieve items
* from using {@link #getBroadcastItem}. Note that only one broadcast can
* be active at a time, so you must be sure to always call this from the
* same thread (usually by scheduling with {@link Handler}) or
@@ -462,56 +219,44 @@ public class RemoteCallbackList<E extends IInterface> {
* <p>A typical loop delivering a broadcast looks like this:
*
* <pre>
- * int i = interfaces.beginBroadcast();
+ * int i = callbacks.beginBroadcast();
* while (i &gt; 0) {
* i--;
* try {
- * interfaces.getBroadcastItem(i).somethingHappened();
+ * callbacks.getBroadcastItem(i).somethingHappened();
* } catch (RemoteException e) {
* // The RemoteCallbackList will take care of removing
* // the dead object for us.
* }
* }
- * interfaces.finishBroadcast();</pre>
+ * callbacks.finishBroadcast();</pre>
*
- * Note that this method is only supported for {@link #FROZEN_CALLEE_POLICY_UNSET}. For other
- * policies use {@link #broadcast(Consumer)} instead.
- *
- * @return Returns the number of interfaces in the broadcast, to be used
+ * @return Returns the number of callbacks in the broadcast, to be used
* with {@link #getBroadcastItem} to determine the range of indices you
* can supply.
*
- * @throws UnsupportedOperationException if an frozen callee policy is set.
- *
* @see #getBroadcastItem
* @see #finishBroadcast
*/
public int beginBroadcast() {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- throw new UnsupportedOperationException();
- }
- return beginBroadcastInternal();
- }
-
- private int beginBroadcastInternal() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mBroadcastCount > 0) {
throw new IllegalStateException(
"beginBroadcast() called while already in a broadcast");
}
- final int n = mBroadcastCount = mInterfaces.size();
- if (n <= 0) {
+ final int N = mBroadcastCount = mCallbacks.size();
+ if (N <= 0) {
return 0;
}
Object[] active = mActiveBroadcast;
- if (active == null || active.length < n) {
- mActiveBroadcast = active = new Object[n];
+ if (active == null || active.length < N) {
+ mActiveBroadcast = active = new Object[N];
}
- for (int i = 0; i < n; i++) {
- active[i] = mInterfaces.valueAt(i);
+ for (int i=0; i<N; i++) {
+ active[i] = mCallbacks.valueAt(i);
}
- return n;
+ return N;
}
}
@@ -522,23 +267,24 @@ public class RemoteCallbackList<E extends IInterface> {
* calling {@link #finishBroadcast}.
*
* <p>Note that it is possible for the process of one of the returned
- * interfaces to go away before you call it, so you will need to catch
+ * callbacks to go away before you call it, so you will need to catch
* {@link RemoteException} when calling on to the returned object.
- * The interface list itself, however, will take care of unregistering
+ * The callback list itself, however, will take care of unregistering
* these objects once it detects that it is no longer valid, so you can
* handle such an exception by simply ignoring it.
*
- * @param index Which of the registered interfaces you would like to
+ * @param index Which of the registered callbacks you would like to
* retrieve. Ranges from 0 to {@link #beginBroadcast}-1, inclusive.
*
- * @return Returns the interface that you can call. This will always be non-null.
+ * @return Returns the callback interface that you can call. This will
+ * always be non-null.
*
* @see #beginBroadcast
*/
public E getBroadcastItem(int index) {
- return ((Interface) mActiveBroadcast[index]).mInterface;
+ return ((Callback)mActiveBroadcast[index]).mCallback;
}
-
+
/**
* Retrieve the cookie associated with the item
* returned by {@link #getBroadcastItem(int)}.
@@ -546,7 +292,7 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #getBroadcastItem
*/
public Object getBroadcastCookie(int index) {
- return ((Interface) mActiveBroadcast[index]).mCookie;
+ return ((Callback)mActiveBroadcast[index]).mCookie;
}
/**
@@ -557,7 +303,7 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #beginBroadcast
*/
public void finishBroadcast() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mBroadcastCount < 0) {
throw new IllegalStateException(
"finishBroadcast() called outside of a broadcast");
@@ -576,18 +322,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} on each registered interface.
+ * Performs {@code action} on each callback, calling
+ * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
- * This is equivalent to #beginBroadcast, followed by iterating over the items using
- * #getBroadcastItem and then @finishBroadcast, except that this method supports
- * frozen callee policies.
+ * @hide
*/
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public void broadcast(@NonNull Consumer<E> callback) {
- int itemCount = beginBroadcastInternal();
+ public void broadcast(Consumer<E> action) {
+ int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- ((Interface) mActiveBroadcast[i]).addCallback(callback);
+ action.accept(getBroadcastItem(i));
}
} finally {
finishBroadcast();
@@ -595,16 +339,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} for each cookie associated with an interface, calling
+ * Performs {@code action} for each cookie associated with a callback, calling
* {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
* @hide
*/
- public <C> void broadcastForEachCookie(Consumer<C> callback) {
+ public <C> void broadcastForEachCookie(Consumer<C> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- callback.accept((C) getBroadcastCookie(i));
+ action.accept((C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -612,16 +356,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} on each interface and associated cookie, calling {@link
+ * Performs {@code action} on each callback and associated cookie, calling {@link
* #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
*
* @hide
*/
- public <C> void broadcast(BiConsumer<E, C> callback) {
+ public <C> void broadcast(BiConsumer<E, C> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- callback.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -629,10 +373,10 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Returns the number of registered interfaces. Note that the number of registered
- * interfaces may differ from the value returned by {@link #beginBroadcast()} since
- * the former returns the number of interfaces registered at the time of the call
- * and the second the number of interfaces to which the broadcast will be delivered.
+ * Returns the number of registered callbacks. Note that the number of registered
+ * callbacks may differ from the value returned by {@link #beginBroadcast()} since
+ * the former returns the number of callbacks registered at the time of the call
+ * and the second the number of callback to which the broadcast will be delivered.
* <p>
* This function is useful to decide whether to schedule a broadcast if this
* requires doing some work which otherwise would not be performed.
@@ -641,39 +385,39 @@ public class RemoteCallbackList<E extends IInterface> {
* @return The size.
*/
public int getRegisteredCallbackCount() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return 0;
}
- return mInterfaces.size();
+ return mCallbacks.size();
}
}
/**
- * Return a currently registered interface. Note that this is
+ * Return a currently registered callback. Note that this is
* <em>not</em> the same as {@link #getBroadcastItem} and should not be used
- * interchangeably with it. This method returns the registered interface at the given
+ * interchangeably with it. This method returns the registered callback at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
* any call to {@link #register} or {@link #unregister} will change these indices, so you
* must do your own thread safety between these to protect from such changes.
*
- * @param index Index of which interface registration to return, from 0 to
+ * @param index Index of which callback registration to return, from 0 to
* {@link #getRegisteredCallbackCount()} - 1.
*
- * @return Returns whatever interface is associated with this index, or null if
+ * @return Returns whatever callback is associated with this index, or null if
* {@link #kill()} has been called.
*/
public E getRegisteredCallbackItem(int index) {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return null;
}
- return mInterfaces.valueAt(index).mInterface;
+ return mCallbacks.valueAt(index).mCallback;
}
}
/**
- * Return any cookie associated with a currently registered interface. Note that this is
+ * Return any cookie associated with a currently registered callback. Note that this is
* <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
* interchangeably with it. This method returns the current cookie registered at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
@@ -687,25 +431,25 @@ public class RemoteCallbackList<E extends IInterface> {
* {@link #kill()} has been called.
*/
public Object getRegisteredCallbackCookie(int index) {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return null;
}
- return mInterfaces.valueAt(index).mCookie;
+ return mCallbacks.valueAt(index).mCookie;
}
}
/** @hide */
public void dump(PrintWriter pw, String prefix) {
- synchronized (mInterfaces) {
- pw.print(prefix); pw.print("callbacks: "); pw.println(mInterfaces.size());
+ synchronized (mCallbacks) {
+ pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
}
}
- private void logExcessiveInterfaces() {
- final long size = mInterfaces.size();
+ private void logExcessiveCallbacks() {
+ final long size = mCallbacks.size();
final long TOO_MANY = 3000;
final long MAX_CHARS = 1000;
if (size >= TOO_MANY) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8a8022c0206a..e940e55bd38b 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API;
import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT;
import android.Manifest;
import android.annotation.FlaggedApi;
@@ -1266,6 +1267,18 @@ public final class Display {
}
/**
+ * Returns whether display supports adaptive refresh rate or not.
+ */
+ // TODO(b/372526856) Add a link to the documentation for ARR.
+ @FlaggedApi(FLAG_ENABLE_HAS_ARR_SUPPORT)
+ public boolean hasArrSupport() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.hasArrSupport;
+ }
+ }
+
+ /**
* <p> Returns true if the connected display can be switched into a mode with minimal
* post processing. </p>
*
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index cac3e3c25098..26fce904eb5e 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -198,6 +198,12 @@ public final class DisplayInfo implements Parcelable {
public float renderFrameRate;
/**
+ * If {@code true} this Display supports adaptive refresh rates.
+ * // TODO(b/372526856) Add a link to the documentation for ARR.
+ */
+ public boolean hasArrSupport;
+
+ /**
* The default display mode.
*/
public int defaultModeId;
@@ -436,6 +442,7 @@ public final class DisplayInfo implements Parcelable {
&& rotation == other.rotation
&& modeId == other.modeId
&& renderFrameRate == other.renderFrameRate
+ && hasArrSupport == other.hasArrSupport
&& defaultModeId == other.defaultModeId
&& userPreferredModeId == other.userPreferredModeId
&& Arrays.equals(supportedModes, other.supportedModes)
@@ -497,6 +504,7 @@ public final class DisplayInfo implements Parcelable {
rotation = other.rotation;
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
+ hasArrSupport = other.hasArrSupport;
defaultModeId = other.defaultModeId;
userPreferredModeId = other.userPreferredModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
@@ -553,6 +561,7 @@ public final class DisplayInfo implements Parcelable {
rotation = source.readInt();
modeId = source.readInt();
renderFrameRate = source.readFloat();
+ hasArrSupport = source.readBoolean();
defaultModeId = source.readInt();
userPreferredModeId = source.readInt();
int nModes = source.readInt();
@@ -626,6 +635,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(rotation);
dest.writeInt(modeId);
dest.writeFloat(renderFrameRate);
+ dest.writeBoolean(hasArrSupport);
dest.writeInt(defaultModeId);
dest.writeInt(userPreferredModeId);
dest.writeInt(supportedModes.length);
@@ -871,6 +881,8 @@ public final class DisplayInfo implements Parcelable {
sb.append(modeId);
sb.append(", renderFrameRate ");
sb.append(renderFrameRate);
+ sb.append(", hasArrSupport ");
+ sb.append(hasArrSupport);
sb.append(", defaultMode ");
sb.append(defaultModeId);
sb.append(", userPreferredModeId ");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 19e244aa5981..e6de478e3d3d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1810,6 +1810,7 @@ public final class SurfaceControl implements Parcelable {
public DisplayMode[] supportedDisplayModes;
public int activeDisplayModeId;
public float renderFrameRate;
+ public boolean hasArrSupport;
public int[] supportedColorModes;
public int activeColorMode;
@@ -1827,6 +1828,7 @@ public final class SurfaceControl implements Parcelable {
+ "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes)
+ ", activeDisplayModeId=" + activeDisplayModeId
+ ", renderFrameRate=" + renderFrameRate
+ + ", hasArrSupport=" + hasArrSupport
+ ", supportedColorModes=" + Arrays.toString(supportedColorModes)
+ ", activeColorMode=" + activeColorMode
+ ", hdrCapabilities=" + hdrCapabilities
@@ -1846,13 +1848,14 @@ public final class SurfaceControl implements Parcelable {
&& Arrays.equals(supportedColorModes, that.supportedColorModes)
&& activeColorMode == that.activeColorMode
&& Objects.equals(hdrCapabilities, that.hdrCapabilities)
- && preferredBootDisplayMode == that.preferredBootDisplayMode;
+ && preferredBootDisplayMode == that.preferredBootDisplayMode
+ && hasArrSupport == that.hasArrSupport;
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId,
- renderFrameRate, activeColorMode, hdrCapabilities);
+ renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b921213cc26c..3be9a821a463 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6971,9 +6971,7 @@ public final class ViewRootImpl implements ViewParent,
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
case MSG_PAUSED_FOR_SYNC_TIMEOUT:
- Log.e(mTag, "Timedout waiting to unpause for sync");
- mNumPausedForSync = 0;
- scheduleTraversals();
+ resumeAfterSyncTimeout();
break;
case MSG_CHECK_INVALIDATION_IDLE: {
long delta;
@@ -12777,6 +12775,15 @@ public final class ViewRootImpl implements ViewParent,
activeSurfaceSyncGroup.addTransaction(t);
}
+ /**
+ * Resume rendering after being paused for sync due to a timeout.
+ */
+ private void resumeAfterSyncTimeout() {
+ Log.e(mTag, "Timedout waiting to unpause for sync mNumPausedForSync=" + mNumPausedForSync);
+ mNumPausedForSync = 0;
+ scheduleTraversals();
+ }
+
@Override
public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
boolean newSyncGroup = false;
@@ -12804,6 +12811,16 @@ public final class ViewRootImpl implements ViewParent,
}
});
newSyncGroup = true;
+
+ // If the sync group is marked ready by a timeout, check if rendering is paused and
+ // if it is, resume rendering and trigger a traversal.
+ mActiveSurfaceSyncGroup.addSyncCompleteCallback(mExecutor, () -> {
+ if (mActiveSurfaceSyncGroup != null
+ && mActiveSurfaceSyncGroup.isComplete() && mNumPausedForSync > 0) {
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ resumeAfterSyncTimeout();
+ }
+ });
}
Trace.instant(Trace.TRACE_TAG_VIEW,
@@ -12818,12 +12835,20 @@ public final class ViewRootImpl implements ViewParent,
}
}
- mNumPausedForSync++;
- mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT,
- 1000 * Build.HW_TIMEOUT_MULTIPLIER);
+ // The sync group can be marked ready by a timeout. This makes incrementing
+ // mNumPausedForSync racy. Here we check if the sync group is complete and
+ // if it is then we don't pause for syncing.
+ if (!mActiveSurfaceSyncGroup.isComplete()) {
+ mNumPausedForSync++;
+ mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT,
+ 1000 * Build.HW_TIMEOUT_MULTIPLIER);
+ } else {
+ Log.d(mTag, "Active sync group is already completed "
+ + mActiveSurfaceSyncGroup.getName());
+ }
return mActiveSurfaceSyncGroup;
- };
+ }
private final Executor mSimpleExecutor = Runnable::run;
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
index 9b396aeea9eb..fc09f880ccd4 100644
--- a/core/java/android/widget/RemoteCollectionItemsAdapter.java
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -40,13 +40,15 @@ class RemoteCollectionItemsAdapter extends BaseAdapter {
private RemoteCollectionItems mItems;
private InteractionHandler mInteractionHandler;
private ColorResources mColorResources;
+ private boolean mOnLightBackground;
private SparseIntArray mLayoutIdToViewType;
RemoteCollectionItemsAdapter(
@NonNull RemoteCollectionItems items,
@NonNull InteractionHandler interactionHandler,
- @NonNull ColorResources colorResources) {
+ @NonNull ColorResources colorResources,
+ boolean onLightBackground) {
// View type count can never increase after an adapter has been set on a ListView.
// Additionally, decreasing it could inhibit view recycling if the count were to back and
// forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for
@@ -56,6 +58,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter {
mItems = items;
mInteractionHandler = interactionHandler;
mColorResources = colorResources;
+ mOnLightBackground = onLightBackground;
initLayoutIdToViewType();
}
@@ -68,7 +71,8 @@ class RemoteCollectionItemsAdapter extends BaseAdapter {
void setData(
@NonNull RemoteCollectionItems items,
@NonNull InteractionHandler interactionHandler,
- @NonNull ColorResources colorResources) {
+ @NonNull ColorResources colorResources,
+ boolean onLightBackground) {
if (mViewTypeCount < items.getViewTypeCount()) {
throw new IllegalArgumentException(
"RemoteCollectionItemsAdapter cannot increase view type count after creation");
@@ -77,6 +81,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter {
mItems = items;
mInteractionHandler = interactionHandler;
mColorResources = colorResources;
+ mOnLightBackground = onLightBackground;
initLayoutIdToViewType();
@@ -184,6 +189,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter {
: new AppWidgetHostView.AdapterChildHostView(parent.getContext());
newView.setInteractionHandler(mInteractionHandler);
newView.setColorResourcesNoReapply(mColorResources);
+ newView.setOnLightBackground(mOnLightBackground);
newView.updateAppWidget(item);
return newView;
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index d7b5211ad9fd..9b6311f35d17 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1248,6 +1248,7 @@ public class RemoteViews implements Parcelable, Filter {
AdapterView adapterView = (AdapterView) target;
Adapter adapter = adapterView.getAdapter();
+ boolean onLightBackground = hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
// We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
// count hasn't increased. Note that AbsListView allocates a fixed size array for view
// recycling in setAdapter, so we must call setAdapter again if the number of view types
@@ -1255,8 +1256,12 @@ public class RemoteViews implements Parcelable, Filter {
if (adapter instanceof RemoteCollectionItemsAdapter
&& adapter.getViewTypeCount() >= items.getViewTypeCount()) {
try {
- ((RemoteCollectionItemsAdapter) adapter).setData(
- items, params.handler, params.colorResources);
+ ((RemoteCollectionItemsAdapter) adapter)
+ .setData(
+ items,
+ params.handler,
+ params.colorResources,
+ onLightBackground);
} catch (Throwable throwable) {
// setData should never failed with the validation in the items builder, but if
// it does, catch and rethrow.
@@ -1266,8 +1271,9 @@ public class RemoteViews implements Parcelable, Filter {
}
try {
- adapterView.setAdapter(new RemoteCollectionItemsAdapter(items,
- params.handler, params.colorResources));
+ adapterView.setAdapter(
+ new RemoteCollectionItemsAdapter(
+ items, params.handler, params.colorResources, onLightBackground));
} catch (Throwable throwable) {
// This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
// a type error.
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 1c3f201c1471..23e572fcd577 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -156,6 +156,22 @@ public final class BackEvent {
}
@Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof BackEvent)) {
+ return false;
+ }
+ final BackEvent that = (BackEvent) other;
+ return mTouchX == that.mTouchX
+ && mTouchY == that.mTouchY
+ && mProgress == that.mProgress
+ && mSwipeEdge == that.mSwipeEdge
+ && mFrameTime == that.mFrameTime;
+ }
+
+ @Override
public String toString() {
return "BackEvent{"
+ "mTouchX=" + mTouchX
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 5d14698c82b3..a68bdc05e20e 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -839,6 +839,16 @@ public final class SurfaceSyncGroup {
}
/**
+ * Returns true if the SurfaceSyncGroup has completed its sync.
+ * @hide
+ */
+ public boolean isComplete() {
+ synchronized (mLock) {
+ return mFinished;
+ }
+ }
+
+ /**
* A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
* implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
* knows when the frame is ready to add to the sync.
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 20d1b3bd12ae..a37bef80ff04 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -83,13 +83,16 @@ public class TaskSnapshot implements Parcelable {
public static final int REFERENCE_CACHE = 1 << 1;
/** This snapshot object is being persistent. */
public static final int REFERENCE_PERSIST = 1 << 2;
+ /** This snapshot object is being used for content suggestion. */
+ public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3;
@IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
REFERENCE_BROADCAST,
REFERENCE_CACHE,
- REFERENCE_PERSIST
+ REFERENCE_PERSIST,
+ REFERENCE_CONTENT_SUGGESTION
})
@Retention(RetentionPolicy.SOURCE)
- @interface ReferenceFlags {}
+ public @interface ReferenceFlags {}
public TaskSnapshot(long id, long captureTime,
@NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 8e495ec1dc40..34abf3114925 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -525,6 +525,22 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Disables or enables activities to be started in adjacent tasks (see
+ * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT}) for the specified root of any child tasks. This
+ * differs from {@link #setLaunchAdjacentFlagRoot(WindowContainerToken)} which controls the
+ * preferred launch-adjacent target and allows for selectively setting which root tasks can
+ * support launch-adjacent.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setDisableLaunchAdjacent(
+ @NonNull WindowContainerToken container, boolean disabled) {
+ mHierarchyOps.add(HierarchyOp.createForSetDisableLaunchAdjacent(container.asBinder(),
+ disabled));
+ return this;
+ }
+
+ /**
* 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.
@@ -1488,6 +1504,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20;
public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21;
public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22;
+ public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1556,6 +1573,8 @@ public final class WindowContainerTransaction implements Parcelable {
private @InsetsType int mExcludeInsetsTypes;
+ private boolean mLaunchAdjacentDisabled;
+
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1644,6 +1663,15 @@ public final class WindowContainerTransaction implements Parcelable {
.build();
}
+ /** Create a hierarchy op for disabling launch adjacent. */
+ public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container,
+ boolean disabled) {
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT)
+ .setContainer(container)
+ .setLaunchAdjacentDisabled(disabled)
+ .build();
+ }
+
/** create a hierarchy op for deleting a task **/
public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)
@@ -1695,6 +1723,7 @@ public final class WindowContainerTransaction implements Parcelable {
mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch;
mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents;
mExcludeInsetsTypes = copy.mExcludeInsetsTypes;
+ mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
}
protected HierarchyOp(Parcel in) {
@@ -1719,6 +1748,7 @@ public final class WindowContainerTransaction implements Parcelable {
mReparentLeafTaskIfRelaunch = in.readBoolean();
mIsTrimmableFromRecents = in.readBoolean();
mExcludeInsetsTypes = in.readInt();
+ mLaunchAdjacentDisabled = in.readBoolean();
}
public int getType() {
@@ -1814,13 +1844,11 @@ public final class WindowContainerTransaction implements Parcelable {
}
/** Denotes whether the parents should also be included in the op. */
- @NonNull
public boolean includingParents() {
return mIncludingParents;
}
- /** Set the task to be trimmable */
- @NonNull
+ /** Denotes whether the task can be trimmable from recents */
public boolean isTrimmableFromRecents() {
return mIsTrimmableFromRecents;
}
@@ -1829,6 +1857,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mExcludeInsetsTypes;
}
+ /** Denotes whether launch-adjacent flag is respected from this task or its children */
+ public boolean isLaunchAdjacentDisabled() {
+ return mLaunchAdjacentDisabled;
+ }
+
/** Gets a string representation of a hierarchy-op type. */
public static String hopToString(int type) {
switch (type) {
@@ -1839,6 +1872,8 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+ return "SetDisableLaunchAdjacent";
case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
@@ -1891,6 +1926,10 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
break;
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+ sb.append("container=").append(mContainer).append(" disabled=")
+ .append(mLaunchAdjacentDisabled);
+ break;
case HIERARCHY_OP_TYPE_START_SHORTCUT:
sb.append("options=").append(mLaunchOptions)
.append(" info=").append(mShortcutInfo);
@@ -1971,6 +2010,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mReparentLeafTaskIfRelaunch);
dest.writeBoolean(mIsTrimmableFromRecents);
dest.writeInt(mExcludeInsetsTypes);
+ dest.writeBoolean(mLaunchAdjacentDisabled);
}
@Override
@@ -2047,6 +2087,8 @@ public final class WindowContainerTransaction implements Parcelable {
private @InsetsType int mExcludeInsetsTypes;
+ private boolean mLaunchAdjacentDisabled;
+
Builder(int type) {
mType = type;
}
@@ -2153,6 +2195,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setLaunchAdjacentDisabled(boolean disabled) {
+ mLaunchAdjacentDisabled = disabled;
+ return this;
+ }
+
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
hierarchyOp.mContainer = mContainer;
@@ -2179,6 +2226,7 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents;
hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes;
+ hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled;
return hierarchyOp;
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 44a374fb7c20..c9d458f22463 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -17,6 +17,7 @@
package android.window;
import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -563,7 +564,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
OnBackAnimationCallback animationCallback = getBackAnimationCallback();
if (animationCallback != null
- && !(callback instanceof ImeBackAnimationController)) {
+ && !(callback instanceof ImeBackAnimationController)
+ && !predictiveBackTimestampApi()) {
mProgressAnimator.onBackInvoked(() -> {
if (mIsSystemCallback) {
mSystemNavigationObserverCallbackRunnable.run();
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 155494fb3b25..18129530978f 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -239,6 +239,13 @@ flag {
}
flag {
+ name: "enable_desktop_windowing_enter_transitions"
+ namespace: "lse_desktop_experience"
+ description: "Enables enter desktop windowing transition & motion polish changes"
+ bug: "369763947"
+}
+
+flag {
name: "enable_desktop_windowing_exit_transitions"
namespace: "lse_desktop_experience"
description: "Enables exit desktop windowing transition & motion polish changes"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 460469c13a3e..0d235ffad9b5 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -70,17 +70,6 @@ flag {
}
flag {
- name: "common_surface_animator"
- namespace: "windowing_frontend"
- description: "A reusable surface animator for default transition"
- bug: "326331384"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "reduce_keyguard_transitions"
namespace: "windowing_frontend"
description: "Avoid setting keyguard transitions ready unless there are no other changes"
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 0a80e006d5bc..dd6c879f1135 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -39,7 +39,7 @@ public class ProtoLogViewerConfigReader {
* or the viewer config is not loaded into memory.
*/
@Nullable
- public synchronized String getViewerString(long messageHash) {
+ public String getViewerString(long messageHash) {
return mLogMessageMap.get(messageHash);
}
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 98e6e8505534..adcc0f64b598 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -39,17 +39,24 @@ import com.android.internal.R;
/**
* An image view that holds the icon displayed at the start of a notification row.
+ * This can generally either display the "small icon" of a notification set via
+ * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set
+ * through {@link this#setIconProvider(NotificationIconProvider)}.
*/
@RemoteViews.RemoteView
public class NotificationRowIconView extends CachingIconView {
+ private NotificationIconProvider mIconProvider;
+
private boolean mApplyCircularCrop = false;
private boolean mShouldShowAppIcon = false;
+ private Drawable mAppIcon = null;
- // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true),
- // to be restored if shouldShowAppIcon becomes false again.
+ // Padding, background and colors set on the view prior to being overridden when showing the app
+ // icon, to be restored if we're showing the small icon again.
private Rect mOriginalPadding = null;
private Drawable mOriginalBackground = null;
-
+ private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+ private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
public NotificationRowIconView(Context context) {
super(context);
@@ -81,6 +88,71 @@ public class NotificationRowIconView extends CachingIconView {
super.onFinishInflate();
}
+ /**
+ * Sets the icon provider for this view. This is used to determine whether we should show the
+ * app icon instead of the small icon, and to fetch the app icon if needed.
+ */
+ public void setIconProvider(NotificationIconProvider iconProvider) {
+ mIconProvider = iconProvider;
+ }
+
+ private Drawable loadAppIcon() {
+ if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) {
+ return mIconProvider.getAppIcon();
+ }
+ return null;
+ }
+
+ @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+ @Override
+ public void setImageIcon(Icon icon) {
+ if (Flags.notificationsRedesignAppIcons()) {
+ if (mAppIcon != null) {
+ // We already know that we should be using the app icon, and we already loaded it.
+ // We assume that cannot change throughout the lifetime of a notification, so
+ // there's nothing to do here.
+ return;
+ }
+ mAppIcon = loadAppIcon();
+ if (mAppIcon != null) {
+ setImageDrawable(mAppIcon);
+ adjustViewForAppIcon();
+ } else {
+ super.setImageIcon(icon);
+ restoreViewForSmallIcon();
+ }
+ return;
+ }
+ super.setImageIcon(icon);
+ }
+
+ @RemotableViewMethod
+ @Override
+ public Runnable setImageIconAsync(Icon icon) {
+ if (Flags.notificationsRedesignAppIcons()) {
+ if (mAppIcon != null) {
+ // We already know that we should be using the app icon, and we already loaded it.
+ // We assume that cannot change throughout the lifetime of a notification, so
+ // there's nothing to do here.
+ return () -> {
+ };
+ }
+ mAppIcon = loadAppIcon();
+ if (mAppIcon != null) {
+ return () -> {
+ setImageDrawable(mAppIcon);
+ adjustViewForAppIcon();
+ };
+ } else {
+ return () -> {
+ super.setImageIcon(icon);
+ restoreViewForSmallIcon();
+ };
+ }
+ }
+ return super.setImageIconAsync(icon);
+ }
+
/** Whether the icon represents the app icon (instead of the small icon). */
@RemotableViewMethod
public void setShouldShowAppIcon(boolean shouldShowAppIcon) {
@@ -91,35 +163,122 @@ public class NotificationRowIconView extends CachingIconView {
mShouldShowAppIcon = shouldShowAppIcon;
if (mShouldShowAppIcon) {
- if (mOriginalPadding == null && mOriginalBackground == null) {
- mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
- getPaddingRight(), getPaddingBottom());
- mOriginalBackground = getBackground();
- }
-
- setPadding(0, 0, 0, 0);
-
- // Make the background white in case the icon itself doesn't have one.
- ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
- PorterDuff.Mode.SRC_ATOP);
-
- if (mOriginalBackground == null) {
- setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
- }
- getBackground().mutate().setColorFilter(colorFilter);
+ adjustViewForAppIcon();
} else {
// Restore original padding and background if needed
- if (mOriginalPadding != null) {
- setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right,
- mOriginalPadding.bottom);
- mOriginalPadding = null;
- }
- setBackground(mOriginalBackground);
- mOriginalBackground = null;
+ restoreViewForSmallIcon();
}
}
}
+ /**
+ * Override padding and background from the view to display the app icon.
+ */
+ private void adjustViewForAppIcon() {
+ removePadding();
+
+ if (Flags.notificationsUseAppIconInRow()) {
+ addWhiteBackground();
+ } else {
+ // No need to set the background for notification redesign, since the icon
+ // factory already does that for us.
+ removeBackground();
+ }
+ }
+
+ /**
+ * Restore padding and background overridden by {@link this#adjustViewForAppIcon}.
+ * Does nothing if they were not overridden.
+ */
+ private void restoreViewForSmallIcon() {
+ restorePadding();
+ restoreBackground();
+ restoreColors();
+ }
+
+ private void removePadding() {
+ if (mOriginalPadding == null) {
+ mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
+ getPaddingRight(), getPaddingBottom());
+ }
+ setPadding(0, 0, 0, 0);
+ }
+
+ private void restorePadding() {
+ if (mOriginalPadding != null) {
+ setPadding(mOriginalPadding.left, mOriginalPadding.top,
+ mOriginalPadding.right,
+ mOriginalPadding.bottom);
+ mOriginalPadding = null;
+ }
+ }
+
+ private void removeBackground() {
+ if (mOriginalBackground == null) {
+ mOriginalBackground = getBackground();
+ }
+
+ setBackground(null);
+ }
+
+ private void addWhiteBackground() {
+ if (mOriginalBackground == null) {
+ mOriginalBackground = getBackground();
+ }
+
+ // Make the background white in case the icon itself doesn't have one.
+ ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
+ PorterDuff.Mode.SRC_ATOP);
+
+ if (mOriginalBackground == null) {
+ setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
+ }
+ getBackground().mutate().setColorFilter(colorFilter);
+ }
+
+ private void restoreBackground() {
+ // NOTE: This will not work if the original background was null, but that's better than
+ // accidentally clearing the background. We expect that there's generally going to be one
+ // anyway unless we manually clear it.
+ if (mOriginalBackground != null) {
+ setBackground(mOriginalBackground);
+ mOriginalBackground = null;
+ }
+ }
+
+ private void restoreColors() {
+ if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) {
+ super.setBackgroundColor(mOriginalBackgroundColor);
+ mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+ }
+ if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) {
+ super.setOriginalIconColor(mOriginalIconColor);
+ mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
+ }
+ }
+
+ @RemotableViewMethod
+ @Override
+ public void setBackgroundColor(int color) {
+ // Ignore color overrides if we're showing the app icon.
+ if (mAppIcon == null) {
+ super.setBackgroundColor(color);
+ } else {
+ mOriginalBackgroundColor = color;
+ }
+ }
+
+ @RemotableViewMethod
+ @Override
+ public void setOriginalIconColor(int color) {
+ // Ignore color overrides if we're showing the app icon.
+ if (mAppIcon == null) {
+ super.setOriginalIconColor(color);
+ } else {
+ mOriginalIconColor = color;
+ }
+ }
+
@Nullable
@Override
Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
@@ -197,4 +356,17 @@ public class NotificationRowIconView extends CachingIconView {
return bitmap;
}
+
+ /**
+ * A provider that allows this view to verify whether it should use the app icon instead of the
+ * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should
+ * primarily be called on the background thread.
+ */
+ public interface NotificationIconProvider {
+ /** Whether this notification should use the app icon instead of the small icon. */
+ boolean shouldShowAppIcon();
+
+ /** Get the app icon for this notification. */
+ Drawable getAppIcon();
+ }
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 9bccf5af7096..8eaa7aa99a2d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -747,16 +747,12 @@ android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint strea
indexMax));
}
-static jint
-android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
- jobject thiz,
- jint stream,
- jint index,
- jint device)
-{
+static jint android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream,
+ jint index, jboolean muted,
+ jint device) {
return check_AudioSystem_Command(
AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index,
- static_cast<audio_devices_t>(device)));
+ muted, static_cast<audio_devices_t>(device)));
}
static jint
@@ -773,13 +769,9 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env,
return index;
}
-static jint
-android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env,
- jobject thiz,
- jobject jaa,
- jint index,
- jint device)
-{
+static jint android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, jobject thiz,
+ jobject jaa, jint index,
+ jboolean muted, jint device) {
// read the AudioAttributes values
JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
@@ -787,7 +779,7 @@ android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env,
return jStatus;
}
return check_AudioSystem_Command(
- AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index,
+ AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, muted,
static_cast<audio_devices_t>(device)));
}
@@ -3448,182 +3440,179 @@ static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env,
#define MAKE_AUDIO_SYSTEM_METHOD(x) \
MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x)
-static const JNINativeMethod gMethods[] =
- {MAKE_AUDIO_SYSTEM_METHOD(setParameters),
- MAKE_AUDIO_SYSTEM_METHOD(getParameters),
- MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone),
- MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted),
- MAKE_AUDIO_SYSTEM_METHOD(isStreamActive),
- MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely),
- MAKE_AUDIO_SYSTEM_METHOD(isSourceActive),
- MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId),
- MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId),
- MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId),
- MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I",
- android_media_AudioSystem_setDeviceConnectionState),
- MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState),
- MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange),
- MAKE_AUDIO_SYSTEM_METHOD(setPhoneState),
- MAKE_AUDIO_SYSTEM_METHOD(setForceUse),
- MAKE_AUDIO_SYSTEM_METHOD(getForceUse),
- MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled),
- MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume),
- MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex),
- MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex),
- MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes",
- "(Landroid/media/AudioAttributes;II)I",
- android_media_AudioSystem_setVolumeIndexForAttributes),
- MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes",
- "(Landroid/media/AudioAttributes;I)I",
- android_media_AudioSystem_getVolumeIndexForAttributes),
- MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes",
- "(Landroid/media/AudioAttributes;)I",
- android_media_AudioSystem_getMinVolumeIndexForAttributes),
- MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes",
- "(Landroid/media/AudioAttributes;)I",
- android_media_AudioSystem_getMaxVolumeIndexForAttributes),
- MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume),
- MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume),
- MAKE_AUDIO_SYSTEM_METHOD(setMasterMute),
- MAKE_AUDIO_SYSTEM_METHOD(getMasterMute),
- MAKE_AUDIO_SYSTEM_METHOD(setMasterMono),
- MAKE_AUDIO_SYSTEM_METHOD(getMasterMono),
- MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance),
- MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance),
- MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate),
- MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount),
- MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency),
- MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice),
- MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger),
- MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V",
- android_media_AudioSystem_setAudioFlingerBinder),
- MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I",
- android_media_AudioSystem_listAudioPorts),
- MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I",
- android_media_AudioSystem_getSupportedDeviceTypes),
- MAKE_JNI_NATIVE_METHOD("createAudioPatch",
- "([Landroid/media/AudioPatch;[Landroid/media/"
- "AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
- android_media_AudioSystem_createAudioPatch),
- MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
- android_media_AudioSystem_releaseAudioPatch),
- MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I",
- android_media_AudioSystem_listAudioPatches),
- MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
- android_media_AudioSystem_setAudioPortConfig),
- MAKE_JNI_NATIVE_METHOD("startAudioSource",
- "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
- android_media_AudioSystem_startAudioSource),
- MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource),
- MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
- MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
- android_media_AudioSystem_registerPolicyMixes),
- MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
- android_media_AudioSystem_getRegisteredPolicyMixes),
- MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
- "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
- "AudioMixingRule;)I",
- android_media_AudioSystem_updatePolicyMixes),
- MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
- android_media_AudioSystem_setUidDeviceAffinities),
- MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities),
- MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback",
- android_media_AudioSystem_registerDynPolicyCallback),
- MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback",
- android_media_AudioSystem_registerRecordingCallback),
- MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback",
- android_media_AudioSystem_registerRoutingCallback),
- MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback",
- android_media_AudioSystem_registerVolRangeInitReqCallback),
- MAKE_AUDIO_SYSTEM_METHOD(systemReady),
- MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB),
- MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support",
- android_media_AudioSystem_getOffloadSupport),
- MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I",
- android_media_AudioSystem_getMicrophones),
- MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I",
- android_media_AudioSystem_getSurroundFormats),
- MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
- android_media_AudioSystem_getReportedSurroundFormats),
- MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled),
- MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids),
- MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids),
- MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids),
- MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported),
- MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported),
- MAKE_JNI_NATIVE_METHOD(
- "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I",
- android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia),
- MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages),
- MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy),
- MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled),
- MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids),
- MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported),
- MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
- android_media_AudioSystem_setDevicesRoleForStrategy),
- MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
- android_media_AudioSystem_removeDevicesRoleForStrategy),
- MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy),
- MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
- android_media_AudioSystem_getDevicesForRoleAndStrategy),
- MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
- android_media_AudioSystem_setDevicesRoleForCapturePreset),
- MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
- android_media_AudioSystem_addDevicesRoleForCapturePreset),
- MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
- android_media_AudioSystem_removeDevicesRoleForCapturePreset),
- MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset),
- MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
- android_media_AudioSystem_getDevicesForRoleAndCapturePreset),
- MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes",
- "(Landroid/media/AudioAttributes;[Landroid/media/"
- "AudioDeviceAttributes;Z)I",
- android_media_AudioSystem_getDevicesForAttributes),
- MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
- android_media_AudioSystem_setUserIdDeviceAffinities),
- MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities),
- MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid),
- MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I",
- android_media_AudioSystem_setVibratorInfos),
- MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer",
- "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
- android_media_AudioSystem_getSpatializer),
- MAKE_JNI_NATIVE_METHOD("canBeSpatialized",
- "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
- "[Landroid/media/AudioDeviceAttributes;)Z",
- android_media_AudioSystem_canBeSpatialized),
- MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose",
- "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;",
- android_media_AudioSystem_nativeGetSoundDose),
- MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport",
- "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
- android_media_AudioSystem_getDirectPlaybackSupport),
- MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes",
- "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
- android_media_AudioSystem_getDirectProfilesForAttributes),
- MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I",
- android_media_AudioSystem_getSupportedMixerAttributes),
- MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes",
- "(Landroid/media/AudioAttributes;IILandroid/media/"
- "AudioMixerAttributes;)I",
- android_media_AudioSystem_setPreferredMixerAttributes),
- MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes",
- "(Landroid/media/AudioAttributes;ILjava/util/List;)I",
- android_media_AudioSystem_getPreferredMixerAttributes),
- MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
- "(Landroid/media/AudioAttributes;II)I",
- android_media_AudioSystem_clearPreferredMixerAttributes),
- MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
- MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
- MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
- MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
- "(Ljava/lang/String;Ljava/lang/Runnable;)J",
- android_media_AudioSystem_listenForSystemPropertyChange),
- MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
- "(J)V",
- android_media_AudioSystem_triggerSystemPropertyUpdate),
-
- };
+static const JNINativeMethod gMethods[] = {
+ MAKE_AUDIO_SYSTEM_METHOD(setParameters),
+ MAKE_AUDIO_SYSTEM_METHOD(getParameters),
+ MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone),
+ MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted),
+ MAKE_AUDIO_SYSTEM_METHOD(isStreamActive),
+ MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely),
+ MAKE_AUDIO_SYSTEM_METHOD(isSourceActive),
+ MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId),
+ MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId),
+ MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId),
+ MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I",
+ android_media_AudioSystem_setDeviceConnectionState),
+ MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState),
+ MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange),
+ MAKE_AUDIO_SYSTEM_METHOD(setPhoneState),
+ MAKE_AUDIO_SYSTEM_METHOD(setForceUse),
+ MAKE_AUDIO_SYSTEM_METHOD(getForceUse),
+ MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled),
+ MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume),
+ MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex),
+ MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex),
+ MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes",
+ "(Landroid/media/AudioAttributes;IZI)I",
+ android_media_AudioSystem_setVolumeIndexForAttributes),
+ MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I",
+ android_media_AudioSystem_getVolumeIndexForAttributes),
+ MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes",
+ "(Landroid/media/AudioAttributes;)I",
+ android_media_AudioSystem_getMinVolumeIndexForAttributes),
+ MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes",
+ "(Landroid/media/AudioAttributes;)I",
+ android_media_AudioSystem_getMaxVolumeIndexForAttributes),
+ MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume),
+ MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume),
+ MAKE_AUDIO_SYSTEM_METHOD(setMasterMute),
+ MAKE_AUDIO_SYSTEM_METHOD(getMasterMute),
+ MAKE_AUDIO_SYSTEM_METHOD(setMasterMono),
+ MAKE_AUDIO_SYSTEM_METHOD(getMasterMono),
+ MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance),
+ MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance),
+ MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate),
+ MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount),
+ MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency),
+ MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice),
+ MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger),
+ MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V",
+ android_media_AudioSystem_setAudioFlingerBinder),
+ MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I",
+ android_media_AudioSystem_listAudioPorts),
+ MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I",
+ android_media_AudioSystem_getSupportedDeviceTypes),
+ MAKE_JNI_NATIVE_METHOD("createAudioPatch",
+ "([Landroid/media/AudioPatch;[Landroid/media/"
+ "AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
+ android_media_AudioSystem_createAudioPatch),
+ MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
+ android_media_AudioSystem_releaseAudioPatch),
+ MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I",
+ android_media_AudioSystem_listAudioPatches),
+ MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
+ android_media_AudioSystem_setAudioPortConfig),
+ MAKE_JNI_NATIVE_METHOD("startAudioSource",
+ "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
+ android_media_AudioSystem_startAudioSource),
+ MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource),
+ MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
+ MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
+ android_media_AudioSystem_registerPolicyMixes),
+ MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
+ android_media_AudioSystem_getRegisteredPolicyMixes),
+ MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
+ "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
+ "AudioMixingRule;)I",
+ android_media_AudioSystem_updatePolicyMixes),
+ MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_setUidDeviceAffinities),
+ MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities),
+ MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback",
+ android_media_AudioSystem_registerDynPolicyCallback),
+ MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback",
+ android_media_AudioSystem_registerRecordingCallback),
+ MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback",
+ android_media_AudioSystem_registerRoutingCallback),
+ MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback",
+ android_media_AudioSystem_registerVolRangeInitReqCallback),
+ MAKE_AUDIO_SYSTEM_METHOD(systemReady),
+ MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB),
+ MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support",
+ android_media_AudioSystem_getOffloadSupport),
+ MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I",
+ android_media_AudioSystem_getMicrophones),
+ MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I",
+ android_media_AudioSystem_getSurroundFormats),
+ MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
+ android_media_AudioSystem_getReportedSurroundFormats),
+ MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled),
+ MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids),
+ MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids),
+ MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids),
+ MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported),
+ MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported),
+ MAKE_JNI_NATIVE_METHOD(
+ "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I",
+ android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia),
+ MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages),
+ MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy),
+ MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled),
+ MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids),
+ MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported),
+ MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_setDevicesRoleForStrategy),
+ MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_removeDevicesRoleForStrategy),
+ MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy),
+ MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+ android_media_AudioSystem_getDevicesForRoleAndStrategy),
+ MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_setDevicesRoleForCapturePreset),
+ MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_addDevicesRoleForCapturePreset),
+ MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_removeDevicesRoleForCapturePreset),
+ MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset),
+ MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+ android_media_AudioSystem_getDevicesForRoleAndCapturePreset),
+ MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes",
+ "(Landroid/media/AudioAttributes;[Landroid/media/"
+ "AudioDeviceAttributes;Z)I",
+ android_media_AudioSystem_getDevicesForAttributes),
+ MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ android_media_AudioSystem_setUserIdDeviceAffinities),
+ MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities),
+ MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid),
+ MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I",
+ android_media_AudioSystem_setVibratorInfos),
+ MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer",
+ "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
+ android_media_AudioSystem_getSpatializer),
+ MAKE_JNI_NATIVE_METHOD("canBeSpatialized",
+ "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
+ "[Landroid/media/AudioDeviceAttributes;)Z",
+ android_media_AudioSystem_canBeSpatialized),
+ MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose",
+ "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;",
+ android_media_AudioSystem_nativeGetSoundDose),
+ MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport",
+ "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
+ android_media_AudioSystem_getDirectPlaybackSupport),
+ MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes",
+ "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
+ android_media_AudioSystem_getDirectProfilesForAttributes),
+ MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I",
+ android_media_AudioSystem_getSupportedMixerAttributes),
+ MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes",
+ "(Landroid/media/AudioAttributes;IILandroid/media/"
+ "AudioMixerAttributes;)I",
+ android_media_AudioSystem_setPreferredMixerAttributes),
+ MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes",
+ "(Landroid/media/AudioAttributes;ILjava/util/List;)I",
+ android_media_AudioSystem_getPreferredMixerAttributes),
+ MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
+ "(Landroid/media/AudioAttributes;II)I",
+ android_media_AudioSystem_clearPreferredMixerAttributes),
+ MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
+ MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
+ MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
+ MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
+ "(Ljava/lang/String;Ljava/lang/Runnable;)J",
+ android_media_AudioSystem_listenForSystemPropertyChange),
+ MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", "(J)V",
+ android_media_AudioSystem_triggerSystemPropertyUpdate),
+};
static const JNINativeMethod gEventHandlerMethods[] =
{MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a939d9274956..755704a5ad91 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -115,6 +115,7 @@ static struct {
jfieldID supportedDisplayModes;
jfieldID activeDisplayModeId;
jfieldID renderFrameRate;
+ jfieldID hasArrSupport;
jfieldID supportedColorModes;
jfieldID activeColorMode;
jfieldID hdrCapabilities;
@@ -1453,7 +1454,7 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jlong disp
env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId,
info.activeDisplayModeId);
env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate);
-
+ env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport);
jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size());
if (colorModesArray == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
@@ -2641,6 +2642,8 @@ int register_android_view_SurfaceControl(JNIEnv* env)
GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I");
gDynamicDisplayInfoClassInfo.renderFrameRate =
GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F");
+ gDynamicDisplayInfoClassInfo.hasArrSupport =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z");
gDynamicDisplayInfoClassInfo.supportedColorModes =
GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I");
gDynamicDisplayInfoClassInfo.activeColorMode =
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
deleted file mode 100644
index 8b2afa86986c..000000000000
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorPrimary" />
-</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
deleted file mode 100644
index cefc9121b7a4..000000000000
--- a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnPrimary" />
-</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
deleted file mode 100644
index eaf9e7d50bbd..000000000000
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorSurfaceContainer" />
-</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
deleted file mode 100644
index 94e50fbe2533..000000000000
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
-</selector> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
deleted file mode 100644
index 0029de14e34a..000000000000
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
- <item>
- <shape android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_background_color"/>
- <corners android:radius="?android:attr/buttonCornerRadius"/>
- <size
- android:width="@dimen/btn_material_width"
- android:height="@dimen/btn_material_height" />
- </shape>
- </item>
-</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
deleted file mode 100644
index 105f077cd841..000000000000
--- a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
- <item>
- <shape android:shape="rectangle">
- <solid android:color="@color/btn_material_filled_tonal_background_color"/>
- <corners android:radius="?android:attr/buttonCornerRadius"/>
- <size
- android:width="@dimen/btn_material_width"
- android:height="@dimen/btn_material_height" />
- </shape>
- </item>
-</ripple> \ No newline at end of file
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
deleted file mode 100644
index 4bc2a66fa206..000000000000
--- a/core/res/res/values-watch-v36/colors.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<!-- TODO(b/372524566): update color token's value to match material3 design. -->
-<resources>
-</resources> \ No newline at end of file
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml
deleted file mode 100644
index c8f347afb318..000000000000
--- a/core/res/res/values-watch-v36/config.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <!-- Overrides system value -->
- <dimen name="config_buttonCornerRadius">26dp</dimen>
-</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
deleted file mode 100644
index ad3c1a3ef3a1..000000000000
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<resources>
- <!-- values for material3 button -->
- <dimen name="btn_material_width">172dp</dimen>
- <dimen name="btn_material_height">52dp</dimen>
- <dimen name="btn_horizontal_edge_padding">14dp</dimen>
- <dimen name="btn_drawable_padding">6dp</dimen>
- <dimen name="btn_lineHeight">18sp</dimen>
- <dimen name="btn_textSize">15sp</dimen>
-
- <!-- Opacity factor for disabled material3 widget -->
- <dimen name="disabled_alpha_device_default">0.12</dimen>
- <dimen name="primary_content_alpha_device_default">0.38</dimen>
-</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
deleted file mode 100644
index 32a22bb755cb..000000000000
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<resources>
- <!-- Button Styles -->
- <!-- Material Button - Filled -->
- <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button">
- <item name="android:background">@drawable/btn_background_material_filled</item>
- <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
- </style>
-
- <!-- Material Button - Filled Tonal(Override system default button styles) -->
- <style name="Widget.DeviceDefault.Button">
- <item name="background">@drawable/btn_background_material_filled_tonal</item>
- <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
- <item name="minHeight">@dimen/btn_material_height</item>
- <item name="maxWidth">@dimen/btn_material_width</item>
- <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
- <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
- <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
- <item name="android:maxLines">2</item>
- <item name="android:ellipsize">end</item>
- <item name="android:breakStrategy">simple</item>
- <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
- <item name="focusable">true</item>
- <item name="clickable">true</item>
- <item name="gravity">center_vertical</item>
- </style>
-
- <!-- Text Styles -->
- <!-- TextAppearance for Material Button - Filled -->
- <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
- <item name="textColor">@color/btn_material_filled_text_color</item>
- </style>
-
- <!-- TextAppearance for Material Button - Filled Tonal -->
- <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
- <item name="android:fontFamily">font-family-flex-device-default</item>
- <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
- <item name="textSize">@dimen/btn_textSize</item>
- <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
- <item name="lineHeight">@dimen/btn_lineHeight</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 48e26203fab4..23a09857032c 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -2397,10 +2397,25 @@ public class NotificationTest {
public void progressStyle_getProgressMax_returnsSumOfSegmentLength() {
final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
progressStyle
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+ new Notification.ProgressStyle.Segment(25)))
.addProgressSegment(new Notification.ProgressStyle.Segment(10))
.addProgressSegment(new Notification.ProgressStyle.Segment(20));
- assertThat(progressStyle.getProgressMax()).isEqualTo(30);
+ assertThat(progressStyle.getProgressMax()).isEqualTo(70);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_getProgressMax_onSetProgressSegments_resets() {
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10))
+ .addProgressSegment(new Notification.ProgressStyle.Segment(20))
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+ new Notification.ProgressStyle.Segment(25)));
+
+ assertThat(progressStyle.getProgressMax()).isEqualTo(40);
}
@Test
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 0621d82747e5..2880ecf835c4 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
+
import static com.android.internal.R.id.pending_intent_tag;
import static org.junit.Assert.assertArrayEquals;
@@ -282,7 +284,10 @@ public class RemoteViewsTest {
widget.addView(view);
ListView listView = (ListView) view.findViewById(R.id.list);
- listView.onRemoteAdapterConnected();
+
+ if (!remoteAdapterConversion()) {
+ listView.onRemoteAdapterConnected();
+ }
assertNotNull(listView.getAdapter());
top.reapply(mContext, view);
@@ -414,6 +419,58 @@ public class RemoteViewsTest {
assertNotNull(view.findViewById(R.id.light_background_text));
}
+ @Test
+ public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagSet() {
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list);
+ RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text);
+ item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+ listRemoteViews.setRemoteAdapter(
+ R.id.list,
+ new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build());
+ container.addView(R.id.container, listRemoteViews);
+
+ widget.setOnLightBackground(true);
+ widget.updateAppWidget(container);
+
+ // Populate the list view
+ ListView listView = (ListView) widget.findViewById(R.id.list);
+ int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
+ listView.measure(measureSpec, measureSpec);
+ listView.layout(0, 0, 100, 100);
+
+ // Picks the light background layout id for the item
+ assertNotNull(listView.getChildAt(0).findViewById(R.id.light_background_text));
+ assertNull(listView.getChildAt(0).findViewById(R.id.text));
+ }
+
+ @Test
+ public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagNotSet() {
+ AppWidgetHostView widget = new AppWidgetHostView(mContext);
+ RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host);
+ RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list);
+ RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text);
+ item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+ listRemoteViews.setRemoteAdapter(
+ R.id.list,
+ new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build());
+ container.addView(R.id.container, listRemoteViews);
+
+ widget.setOnLightBackground(false);
+ widget.updateAppWidget(container);
+
+ // Populate the list view
+ ListView listView = (ListView) widget.findViewById(R.id.list);
+ int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
+ listView.measure(measureSpec, measureSpec);
+ listView.layout(0, 0, 100, 100);
+
+ // Does not pick the light background layout id for the item
+ assertNotNull(listView.getChildAt(0).findViewById(R.id.text));
+ assertNull(listView.getChildAt(0).findViewById(R.id.light_background_text));
+ }
+
private RemoteViews createViewChained(int depth, String... texts) {
RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index 61cd1c323ac1..5a2a723cacee 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -61,11 +61,30 @@ public class SplitScreenConstants {
@IntDef(prefix = {"SPLIT_POSITION_"}, value = {
SPLIT_POSITION_UNDEFINED,
SPLIT_POSITION_TOP_OR_LEFT,
- SPLIT_POSITION_BOTTOM_OR_RIGHT
+ SPLIT_POSITION_BOTTOM_OR_RIGHT,
})
public @interface SplitPosition {
}
+ // These SPLIT_INDEX constants will be used in the same way as the above SPLIT_POSITION ints,
+ // but scalable to n apps. Eventually, SPLIT_POSITION can be deprecated and only the below
+ // will be used.
+ public static final int SPLIT_INDEX_UNDEFINED = -1;
+ public static final int SPLIT_INDEX_0 = 0;
+ public static final int SPLIT_INDEX_1 = 1;
+ public static final int SPLIT_INDEX_2 = 2;
+ public static final int SPLIT_INDEX_3 = 3;
+
+ @IntDef(prefix = {"SPLIT_INDEX_"}, value = {
+ SPLIT_INDEX_UNDEFINED,
+ SPLIT_INDEX_0,
+ SPLIT_INDEX_1,
+ SPLIT_INDEX_2,
+ SPLIT_INDEX_3
+ })
+ public @interface SplitIndex {
+ }
+
/**
* A snap target for two apps, where the split is 33-66. With FLAG_ENABLE_FLEXIBLE_SPLIT,
* only used on tablets.
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 a8a8c2a80974..eb9cdab6e856 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
@@ -1246,9 +1246,12 @@ public class BubbleController implements ConfigurationChangeListener,
*/
public void dragBubbleToDismiss(String bubbleKey, long timestamp) {
String selectedBubbleKey = mBubbleData.getSelectedBubbleKey();
- if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) {
+ Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey);
+ if (bubbleToDismiss != null) {
mBubbleData.dismissBubbleWithKey(
bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp);
+ mLogger.log(bubbleToDismiss,
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE);
}
if (mBubbleData.hasBubbles()) {
// We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
@@ -1979,11 +1982,15 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void addBubble(Bubble addedBubble) {
+ // Only log metrics event
+ mLogger.log(addedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED);
// Nothing to do for adds, these are handled by launcher / in the bubble bar.
}
@Override
public void updateBubble(Bubble updatedBubble) {
+ // Only log metrics event
+ mLogger.log(updatedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED);
// Nothing to do for updates, these are handled by launcher / in the bubble bar.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 1abe11998500..6d757d26a9bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.bubbles;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
@@ -33,7 +32,6 @@ public class BubbleLogger {
/**
* Bubble UI event.
*/
- @VisibleForTesting
public enum Event implements UiEventLogger.UiEventEnum {
// region bubble events
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 9f100facc163..0b2b3e7c41c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -19,9 +19,11 @@ package com.android.wm.shell.common.split;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE;
@@ -33,6 +35,9 @@ import android.graphics.Rect;
import androidx.annotation.Nullable;
+import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+
import java.util.ArrayList;
/**
@@ -79,7 +84,9 @@ public class DividerSnapAlgorithm {
private final int mTaskHeightInMinimizedMode;
private final float mFixedRatio;
/** Allows split ratios to calculated dynamically instead of using {@link #mFixedRatio}. */
- private final boolean mAllowFlexibleSplitRatios;
+ private final boolean mCalculateRatiosBasedOnAvailableSpace;
+ /** Allows split ratios that go offscreen (a.k.a. "flexible split") */
+ private final boolean mAllowOffscreenRatios;
private final boolean mIsHorizontalDivision;
/** The first target which is still splitting the screen */
@@ -119,8 +126,11 @@ public class DividerSnapAlgorithm {
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
mMinimalSizeResizableTask = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_resizable_task);
- mAllowFlexibleSplitRatios = res.getBoolean(
+ mCalculateRatiosBasedOnAvailableSpace = res.getBoolean(
com.android.internal.R.bool.config_flexibleSplitRatios);
+ // If this is a small screen or a foldable, use offscreen ratios
+ mAllowOffscreenRatios =
+ Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res);
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
calculateTargets(isHorizontalDivision, dockSide);
@@ -233,6 +243,11 @@ public class DividerSnapAlgorithm {
return mFirstSplitTarget.position < position && position < mLastSplitTarget.position;
}
+ /** Returns if we are currently on a device/screen that supports split apps going offscreen. */
+ public boolean areOffscreenRatiosSupported() {
+ return mAllowOffscreenRatios;
+ }
+
private SnapTarget snap(int position, boolean hardDismiss) {
if (shouldApplyFreeSnapMode(position)) {
return new SnapTarget(position, SNAP_TO_NONE);
@@ -283,10 +298,14 @@ public class DividerSnapAlgorithm {
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
int bottomPosition, int dividerMax) {
- maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_2_33_66);
+ @PersistentSnapPosition int firstTarget =
+ mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
+ @PersistentSnapPosition int lastTarget =
+ mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
+ maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget);
addMiddleTarget(isHorizontalDivision);
maybeAddTarget(bottomPosition,
- dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_2_66_33);
+ dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget);
}
private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
@@ -295,7 +314,11 @@ public class DividerSnapAlgorithm {
? mDisplayHeight - mInsets.bottom
: mDisplayWidth - mInsets.right;
int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
- if (mAllowFlexibleSplitRatios) {
+
+ if (mAllowOffscreenRatios) {
+ // TODO (b/349828130): This is a placeholder value, real measurements to come
+ size = (int) (0.3f * (end - start)) - mDividerSize / 2;
+ } else if (mCalculateRatiosBasedOnAvailableSpace) {
size = Math.max(size, mMinimalSizeResizableTask);
}
int topPosition = start + size;
@@ -324,7 +347,7 @@ public class DividerSnapAlgorithm {
* meets the minimal size requirement.
*/
private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
- if (smallerSize >= mMinimalSizeResizableTask) {
+ if (smallerSize >= mMinimalSizeResizableTask || mAllowOffscreenRatios) {
mTargets.add(new SnapTarget(position, snapPosition));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 83ffaf473999..c9c3aa0dd537 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -30,6 +30,8 @@ import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLAT
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
import static com.android.wm.shell.shared.animation.Interpolators.LINEAR;
import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -438,12 +440,31 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
dividerBounds.right = dividerBounds.left + mDividerWindowWidth;
bounds1.right = position;
bounds2.left = bounds1.right + mDividerSize;
+
+ // For flexible split, expand app offscreen as well
+ if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
+ if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
+ bounds1.left = bounds1.right - bounds2.width();
+ } else {
+ bounds2.right = bounds2.left + bounds1.width();
+ }
+ }
+
} else {
position += mRootBounds.top;
dividerBounds.top = position - mDividerInsets;
dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth;
bounds1.bottom = position;
bounds2.top = bounds1.bottom + mDividerSize;
+
+ // For flexible split, expand app offscreen as well
+ if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
+ if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
+ bounds1.top = bounds1.bottom - bounds2.width();
+ } else {
+ bounds2.bottom = bounds2.top + bounds1.width();
+ }
+ }
}
DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
@@ -669,6 +690,21 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
});
}
+ /**
+ * Moves the divider to the other side of the screen. Does nothing if the divider is in the
+ * center.
+ * TODO (b/349828130): Currently only supports the two-app case. For n-apps,
+ * DividerSnapAlgorithm will need to be refactored, and this function will change as well.
+ */
+ public void flingDividerToOtherSide(@PersistentSnapPosition int currentSnapPosition) {
+ switch (currentSnapPosition) {
+ case SNAP_TO_2_10_90 ->
+ snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget());
+ case SNAP_TO_2_90_10 ->
+ snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget());
+ }
+ }
+
@VisibleForTesting
void flingDividerPosition(int from, int to, int duration,
@Nullable Runnable flingFinishedCallback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index bdbcb4635ae8..65bf389f3819 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -18,6 +18,10 @@ package com.android.wm.shell.common.split;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_45_45_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -38,6 +42,8 @@ import com.android.wm.shell.shared.split.SplitScreenConstants;
/** Helper utility class for split screen components to use. */
public class SplitScreenUtils {
+ private static final int LARGE_SCREEN_MIN_EDGE_DP = 600;
+
/** Reverse the split position. */
@SplitScreenConstants.SplitPosition
public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) {
@@ -110,10 +116,9 @@ public class SplitScreenUtils {
Configuration config) {
// Compare the max bounds sizes as on near-square devices, the insets may result in a
// configuration in the other orientation
- final boolean isLargeScreen = config.smallestScreenWidthDp >= 600;
final Rect maxBounds = config.windowConfiguration.getMaxBounds();
final boolean isLandscape = maxBounds.width() >= maxBounds.height();
- return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen, isLandscape);
+ return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen(config), isLandscape);
}
/**
@@ -128,4 +133,41 @@ public class SplitScreenUtils {
return isLandscape;
}
}
+
+ /**
+ * Returns whether the current config is a large screen (tablet or unfolded foldable)
+ */
+ public static boolean isLargeScreen(Configuration config) {
+ return config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP;
+ }
+
+ /**
+ * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone
+ * or a foldable (either screen), we allow it.
+ */
+ public static boolean allowOffscreenRatios(Resources res) {
+ return !isLargeScreen(res.getConfiguration())
+ ||
+ res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+ }
+
+ /**
+ * Within a particular split layout, we label the stages numerically: 0, 1, 2... from left to
+ * right (or top to bottom). This function takes in a stage index (0th, 1st, 2nd...) and a
+ * PersistentSnapPosition and returns if that particular stage is offscreen in that layout.
+ */
+ public static boolean isPartiallyOffscreen(int stageIndex,
+ @SplitScreenConstants.PersistentSnapPosition int snapPosition) {
+ switch(snapPosition) {
+ case SNAP_TO_2_10_90:
+ case SNAP_TO_3_10_45_45:
+ return stageIndex == 0;
+ case SNAP_TO_2_90_10:
+ return stageIndex == 1;
+ case SNAP_TO_3_45_45_10:
+ return stageIndex == 2;
+ default:
+ return false;
+ }
+ }
}
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 57a59c946f98..b723337cc894 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
@@ -659,6 +659,7 @@ class DesktopTasksController(
}
val wct = WindowContainerTransaction()
+ if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId)
wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
@@ -1245,7 +1246,7 @@ class DesktopTasksController(
val bounds = when (newTaskWindowingMode) {
WINDOWING_MODE_FREEFORM -> {
displayController.getDisplayLayout(callingTask.displayId)
- ?.let { getInitialBounds(it, callingTask) }
+ ?.let { getInitialBounds(it, callingTask, callingTask.displayId) }
}
WINDOWING_MODE_MULTI_WINDOW -> {
Rect()
@@ -1311,7 +1312,7 @@ class DesktopTasksController(
val displayLayout = displayController.getDisplayLayout(task.displayId)
if (displayLayout != null) {
val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
- cascadeWindow(task, initialBounds, displayLayout)
+ cascadeWindow(initialBounds, displayLayout, task.displayId)
wct.setBounds(task.token, initialBounds)
}
}
@@ -1399,13 +1400,19 @@ class DesktopTasksController(
return if (wct.isEmpty) null else wct
}
+ /**
+ * Apply all changes required when task is first added to desktop. Uses the task's current
+ * display by default to apply initial bounds and placement relative to the display.
+ * Use a different [displayId] if the task should be moved to a different display.
+ */
@VisibleForTesting
fun addMoveToDesktopChanges(
wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo
+ taskInfo: RunningTaskInfo,
+ displayId: Int = taskInfo.displayId,
) {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: return
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
val targetWindowingMode =
if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1414,7 +1421,7 @@ class DesktopTasksController(
} else {
WINDOWING_MODE_FREEFORM
}
- val initialBounds = getInitialBounds(displayLayout, taskInfo)
+ val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId)
if (canChangeTaskPosition(taskInfo)) {
wct.setBounds(taskInfo.token, initialBounds)
@@ -1428,7 +1435,8 @@ class DesktopTasksController(
private fun getInitialBounds(
displayLayout: DisplayLayout,
- taskInfo: RunningTaskInfo
+ taskInfo: RunningTaskInfo,
+ displayId: Int,
): Rect {
val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
calculateInitialBounds(displayLayout, taskInfo)
@@ -1437,7 +1445,7 @@ class DesktopTasksController(
}
if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
- cascadeWindow(taskInfo, bounds, displayLayout)
+ cascadeWindow(bounds, displayLayout, displayId)
}
return bounds
}
@@ -1466,11 +1474,11 @@ class DesktopTasksController(
}
}
- private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) {
+ private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
- val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId)
+ val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
activeTasks.firstOrNull()?.let { activeTask ->
shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
cascadeWindow(context.resources, stableBounds,
@@ -2069,6 +2077,12 @@ class DesktopTasksController(
c.removeDesktop(displayId)
}
}
+
+ override fun moveToExternalDisplay(taskId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "moveTaskToExternalDisplay") { c ->
+ c.moveToNextDisplay(taskId)
+ }
+ }
}
private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 8e264b2410f7..34c2f1e760a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -648,7 +648,13 @@ sealed class DragToDesktopTransitionHandler(
state.startAborted = true
// The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction.
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
- } else if (state.cancelTransitionToken != transition) {
+ } else if (state.cancelTransitionToken == transition) {
+ state.draggedTaskChange?.leash?.let {
+ state.startTransitionFinishTransaction?.show(it)
+ }
+ state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+ clearState()
+ } else {
// This transition being aborted is neither the start, nor the cancel transition, so
// it must be the finish transition (DRAG_RELEASE); cancel its jank interaction.
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
@@ -863,7 +869,8 @@ sealed class DragToDesktopTransitionHandler(
companion object {
/** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
- internal const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 86351e364cdd..c27813de5358 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -52,4 +52,7 @@ interface IDesktopMode {
/** Remove desktop on the given display */
oneway void removeDesktop(int displayId);
+
+ /** Move a task with given `taskId` to external display */
+ void moveToExternalDisplay(int taskId);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 268c3a20a41a..537ef182bb04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -350,7 +350,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */);
- // mPipTaskOrganizer.removePip();
+ mPipScheduler.removePipAfterAnimation();
}
/** Sets the movement bounds to use to constrain PIP position animations. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index d4f190ebd2a2..fbbf6f3596d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -19,81 +19,40 @@ package com.android.wm.shell.pip2.phone;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
-import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Scheduler for Shell initiated PiP transitions and animations.
*/
public class PipScheduler {
private static final String TAG = PipScheduler.class.getSimpleName();
- private static final String BROADCAST_FILTER = PipScheduler.class.getCanonicalName();
private final Context mContext;
private final PipBoundsState mPipBoundsState;
private final ShellExecutor mMainExecutor;
private final PipTransitionState mPipTransitionState;
- private PipSchedulerReceiver mSchedulerReceiver;
private PipTransitionController mPipTransitionController;
+ private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
@Nullable private Runnable mUpdateMovementBoundsRunnable;
- /**
- * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
- * This is used for a broadcast receiver to resolve intents. This should be removed once
- * there is an equivalent of PipTouchHandler and PipResizeGestureHandler for PiP2.
- */
- private static final int PIP_EXIT_VIA_EXPAND_CODE = 0;
- private static final int PIP_DOUBLE_TAP = 1;
-
- @IntDef(value = {
- PIP_EXIT_VIA_EXPAND_CODE,
- PIP_DOUBLE_TAP
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface PipUserJourneyCode {}
-
- /**
- * A temporary broadcast receiver to initiate PiP CUJs.
- */
- private class PipSchedulerReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- int userJourneyCode = intent.getIntExtra("cuj_code_extra", 0);
- switch (userJourneyCode) {
- case PIP_EXIT_VIA_EXPAND_CODE:
- scheduleExitPipViaExpand();
- break;
- case PIP_DOUBLE_TAP:
- scheduleDoubleTapToResize();
- break;
- default:
- throw new IllegalStateException("unexpected CUJ code=" + userJourneyCode);
- }
- }
- }
-
public PipScheduler(Context context,
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
@@ -103,12 +62,8 @@ public class PipScheduler {
mMainExecutor = mainExecutor;
mPipTransitionState = pipTransitionState;
- if (PipUtils.isPip2ExperimentEnabled()) {
- // temporary broadcast receiver to initiate exit PiP via expand
- mSchedulerReceiver = new PipSchedulerReceiver();
- ContextCompat.registerReceiver(mContext, mSchedulerReceiver,
- new IntentFilter(BROADCAST_FILTER), ContextCompat.RECEIVER_EXPORTED);
- }
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
}
ShellExecutor getMainExecutor() {
@@ -133,6 +88,18 @@ public class PipScheduler {
return wct;
}
+ @Nullable
+ private WindowContainerTransaction getRemovePipTransaction() {
+ if (mPipTransitionState.mPipTaskToken == null) {
+ return null;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mPipTransitionState.mPipTaskToken, null);
+ wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(mPipTransitionState.mPipTaskToken, false);
+ return wct;
+ }
+
/**
* Schedules exit PiP via expand transition.
*/
@@ -146,10 +113,26 @@ public class PipScheduler {
}
}
- /**
- * Schedules resize PiP via double tap.
- */
- public void scheduleDoubleTapToResize() {}
+ // TODO: Optimize this by running the animation as part of the transition
+ /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */
+ public void removePipAfterAnimation() {
+ SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext,
+ mPipTransitionState.mPinnedTaskLeash, tx, PipAlphaAnimator.FADE_OUT);
+ animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
+ animator.start();
+ }
+
+ /** Schedules remove PiP transition. */
+ private void scheduleRemovePipImmediately() {
+ WindowContainerTransaction wct = getRemovePipTransaction();
+ if (wct != null) {
+ mMainExecutor.execute(() -> {
+ mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
+ null /* destinationBounds */);
+ });
+ }
+ }
/**
* Animates resizing of the pinned stack given the duration.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index c58de2c3512a..373ec806c40c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -90,9 +90,10 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
if (mPictureInPictureParams.equals(params)) {
return;
}
- if (PipUtils.remoteActionsChanged(params.getActions(), mPictureInPictureParams.getActions())
+ if (params != null && (PipUtils.remoteActionsChanged(params.getActions(),
+ mPictureInPictureParams.getActions())
|| !PipUtils.remoteActionsMatch(params.getCloseAction(),
- mPictureInPictureParams.getCloseAction())) {
+ mPictureInPictureParams.getCloseAction()))) {
for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
listener.onActionsChanged(params.getActions(), params.getCloseAction());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 029f001401c5..a4a7973ef4bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -582,7 +582,6 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
return true;
}
- /*
if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
&& mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
@@ -599,6 +598,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
return true;
}
+ /*
// Ignore the motion event When the entry animation is waiting to be started
if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) {
ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index b57f51aff176..ac1567aba6e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
import android.animation.Animator;
@@ -605,8 +606,11 @@ public class PipTransition extends PipTransitionController implements
&& pipChange.getMode() == TRANSIT_TO_BACK;
boolean isPipClosed = info.getType() == TRANSIT_CLOSE
&& pipChange.getMode() == TRANSIT_CLOSE;
- // PiP is being removed if the pinned task is either moved to back or closed.
- return isPipMovedToBack || isPipClosed;
+ // If PiP is dismissed by user (i.e. via dismiss button in PiP menu)
+ boolean isPipDismissed = info.getType() == TRANSIT_REMOVE_PIP
+ && pipChange.getMode() == TRANSIT_TO_BACK;
+ // PiP is being removed if the pinned task is either moved to back, closed, or dismissed.
+ return isPipMovedToBack || isPipClosed || isPipDismissed;
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 47c5eec8cbd1..3e76403de51b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -35,12 +35,19 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitScreenUtils.isPartiallyOffscreen;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
+import static com.android.wm.shell.shared.TransitionUtil.isOrderOnly;
import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -117,6 +124,7 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.policy.FoldLockSettingsObserver;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -160,19 +168,19 @@ import java.util.concurrent.Executor;
* - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are
* visible
* - Both stages are put under a single-top root task.
- * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
- * {@link #onStageHasChildrenChanged(StageListenerImpl).}
*/
public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
- ShellTaskOrganizer.TaskListener {
+ ShellTaskOrganizer.TaskListener, StageTaskListener.StageListenerCallbacks {
private static final String TAG = StageCoordinator.class.getSimpleName();
+ // The duration in ms to prevent launch-adjacent from working after split screen is first
+ // entered
+ private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000;
+
private final StageTaskListener mMainStage;
- private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final StageTaskListener mSideStage;
- private final StageListenerImpl mSideStageListener = new StageListenerImpl();
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -235,6 +243,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private SplitScreen.SplitInvocationListener mSplitInvocationListener;
private Executor mSplitInvocationListenerExecutor;
+ // Re-enables launch-adjacent handling on the split root task. This needs to be a member
+ // because we will be posting and removing it from the handler.
+ private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false);
+
/**
* Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
* CompatUI layouts. CompatUI is handled separately by MainStage and SideStage.
@@ -328,7 +340,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mContext,
mTaskOrganizer,
mDisplayId,
- mMainStageListener,
+ this /*stageListenerCallbacks*/,
mSyncQueue,
iconProvider,
mWindowDecorViewModel);
@@ -336,7 +348,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mContext,
mTaskOrganizer,
mDisplayId,
- mSideStageListener,
+ this /*stageListenerCallbacks*/,
mSyncQueue,
iconProvider,
mWindowDecorViewModel);
@@ -411,7 +423,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
public boolean isSplitScreenVisible() {
- return mSideStageListener.mVisible && mMainStageListener.mVisible;
+ return mSideStage.mVisible && mMainStage.mVisible;
}
private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
@@ -1096,7 +1108,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
- if (mSideStageListener.mVisible && updateBounds) {
+ if (mSideStage.mVisible && updateBounds) {
if (wct == null) {
// onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
onLayoutSizeChanged(mSplitLayout);
@@ -1317,8 +1329,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void clearRequestIfPresented() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
- if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
- && mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
+ if (mSideStage.mVisible && mSideStage.mHasChildren
+ && mMainStage.mVisible && mSideStage.mHasChildren) {
mSplitRequest = null;
}
}
@@ -1571,11 +1583,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
+ @Override
+ public void onChildTaskStatusChanged(StageTaskListener stageListener, int taskId,
boolean present, boolean visible) {
int stage;
if (present) {
- stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
} else {
// No longer on any stage
stage = STAGE_TYPE_UNDEFINED;
@@ -1706,13 +1719,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@VisibleForTesting
- void onRootTaskAppeared() {
+ @Override
+ public void onRootTaskAppeared() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
- mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
+ mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask);
// Wait unit all root tasks appeared.
if (mRootTaskInfo == null
- || !mMainStageListener.mHasRootTask
- || !mSideStageListener.mHasRootTask) {
+ || !mMainStage.mHasRootTask
+ || !mSideStage.mHasRootTask) {
return;
}
@@ -1732,35 +1746,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
}
- /** Callback when split roots have child task appeared under it, this is a little different from
- * #onStageHasChildrenChanged because this would be called every time child task appeared.
- * NOTICE: This only be called on legacy transition. */
- private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
- stageListener == mMainStageListener, taskId);
- // Handle entering split screen while there is a split pair running in the background.
- if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
- && mSplitRequest == null) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareEnterSplitScreen(wct);
- mMainStage.evictAllChildren(wct);
- mSideStage.evictOtherChildren(wct, taskId);
-
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> {
- if (mIsDropEntering) {
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- mIsDropEntering = false;
- mSkipEvictingMainStageChildren = false;
- } else {
- mShowDecorImmediately = true;
- mSplitLayout.flingDividerToCenter(/*finishCallback*/ null);
- }
- });
- }
- }
-
- private void onRootTaskVanished() {
+ @Override
+ public void onRootTaskVanished() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
final WindowContainerTransaction wct = new WindowContainerTransaction();
mLaunchAdjacentController.clearLaunchAdjacentRoot();
@@ -1777,15 +1764,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Callback when split roots visiblility changed.
* NOTICE: This only be called on legacy transition. */
- private void onStageVisibilityChanged(StageListenerImpl stageListener) {
+ @Override
+ public void onStageVisibilityChanged(StageTaskListener stageListener) {
// If split didn't active, just ignore this callback because we should already did these
// on #applyExitSplitScreen.
if (!isSplitActive()) {
return;
}
- final boolean sideStageVisible = mSideStageListener.mVisible;
- final boolean mainStageVisible = mMainStageListener.mVisible;
+ final boolean sideStageVisible = mSideStage.mVisible;
+ final boolean mainStageVisible = mMainStage.mVisible;
// Wait for both stages having the same visibility to prevent causing flicker.
if (mainStageVisible != sideStageVisible) {
@@ -1922,18 +1910,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Callback when split roots have child or haven't under it.
* NOTICE: This only be called on legacy transition. */
- private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+ @Override
+ public void onStageHasChildrenChanged(StageTaskListener stageListener) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
- stageListener == mMainStageListener);
+ stageListener == mMainStage);
final boolean hasChildren = stageListener.mHasChildren;
- final boolean isSideStage = stageListener == mSideStageListener;
+ final boolean isSideStage = stageListener == mSideStage;
if (!hasChildren && !mIsExiting && isSplitActive()) {
- if (isSideStage && mMainStageListener.mVisible) {
+ if (isSideStage && mMainStage.mVisible) {
// Exit to main stage if side stage no longer has children.
mSplitLayout.flingDividerToDismiss(
mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
- } else if (!isSideStage && mSideStageListener.mVisible) {
+ } else if (!isSideStage && mSideStage.mVisible) {
// Exit to side stage if main stage no longer has children.
mSplitLayout.flingDividerToDismiss(
mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
@@ -1958,7 +1947,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
});
}
- if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
+ if (mMainStage.mHasChildren && mSideStage.mHasChildren) {
mShouldUpdateRecents = true;
clearRequestIfPresented();
updateRecentTasksSplitPair();
@@ -1974,6 +1963,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
+ public void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
+ ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
+ if (isSplitActive()) {
+ final boolean isMainStage = mMainStage == stageTaskListener;
+
+ // If visible, we preserve the app and keep it running. If an app becomes
+ // unsupported in the bg, break split without putting anything on top
+ boolean splitScreenVisible = isSplitScreenVisible();
+ int stageType = STAGE_TYPE_UNDEFINED;
+ if (splitScreenVisible) {
+ stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(stageType, wct);
+ clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
+ "app package " + taskInfo.baseIntent.getComponent()
+ + " does not support splitscreen, or is a controlled activity"
+ + " type"));
+ if (splitScreenVisible) {
+ handleUnsupportedSplitStart();
+ }
+ }
+ }
+
+ @Override
public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
bottomOrRight, exitReasonToString(exitReason));
@@ -2045,6 +2063,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
}, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager());
+ if (Flags.enableFlexibleTwoAppSplit()) {
+ switch (layout.calculateCurrentSnapPosition()) {
+ case SNAP_TO_2_10_90 -> grantFocusToPosition(false /* leftOrTop */);
+ case SNAP_TO_2_90_10 -> grantFocusToPosition(true /* leftOrTop */);
+ }
+ }
+
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
@@ -2498,6 +2523,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
continue;
+ } else if (Flags.enableFlexibleTwoAppSplit() && isOrderOnly(change)) {
+ int focusedStageIndex = SPLIT_INDEX_UNDEFINED;
+ if (taskInfo.token.equals(mMainStage.mRootTaskInfo.token)) {
+ focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? SPLIT_INDEX_0 : SPLIT_INDEX_1;
+ } else if (taskInfo.token.equals(mSideStage.mRootTaskInfo.token)) {
+ focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? SPLIT_INDEX_1 : SPLIT_INDEX_0;
+ }
+
+ if (focusedStageIndex != SPLIT_INDEX_UNDEFINED) {
+ @PersistentSnapPosition int currentSnapPosition =
+ mSplitLayout.calculateCurrentSnapPosition();
+ boolean offscreenTaskFocused =
+ isPartiallyOffscreen(focusedStageIndex, currentSnapPosition);
+
+ if (offscreenTaskFocused) {
+ mSplitLayout.flingDividerToOtherSide(currentSnapPosition);
+ }
+ }
}
final StageTaskListener stage = getStageOfTask(taskInfo);
if (stage == null) {
@@ -2662,6 +2707,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ /**
+ * Sets whether launch-adjacent is disabled or enabled.
+ */
+ private void setLaunchAdjacentDisabled(boolean disabled) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLaunchAdjacentDisabled: disabled=%b", disabled);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDisableLaunchAdjacent(mRootTaskInfo.token, disabled);
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
/** Starts the pending transition animation. */
public boolean startPendingAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@@ -2674,6 +2729,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition,
mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
+
+ // Disable launch adjacent after an enter animation to prevent cases where apps are
+ // incorrectly trampolining and incorrectly triggering a double launch-adjacent task
+ // launch (ie. main -> split -> main). See b/344216031
+ setLaunchAdjacentDisabled(true);
+ mMainHandler.removeCallbacks(mReEnableLaunchAdjacentOnRoot);
+ mMainHandler.postDelayed(mReEnableLaunchAdjacentOnRoot,
+ DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
shouldAnimate = startPendingDismissAnimation(
@@ -3182,13 +3245,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
pw.println(childPrefix + "isActive=" + isSplitActive());
mMainStage.dump(pw, childPrefix);
- pw.println(innerPrefix + "MainStageListener");
- mMainStageListener.dump(pw, childPrefix);
pw.println(innerPrefix + "SideStage");
pw.println(childPrefix + "stagePosition=" + splitPositionToString(getSideStagePosition()));
mSideStage.dump(pw, childPrefix);
- pw.println(innerPrefix + "SideStageListener");
- mSideStageListener.dump(pw, childPrefix);
if (mSplitLayout != null) {
mSplitLayout.dump(pw, childPrefix);
}
@@ -3204,8 +3263,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
*/
private void setSplitsVisible(boolean visible) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
- mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
- mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
+ mMainStage.mVisible = mSideStage.mVisible = visible;
+ mMainStage.mHasChildren = mSideStage.mHasChildren = visible;
}
/**
@@ -3255,86 +3314,4 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
!toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
mSplitLayout.isLeftRightSplit());
}
-
- class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
- boolean mHasRootTask = false;
- boolean mVisible = false;
- boolean mHasChildren = false;
-
- @Override
- public void onRootTaskAppeared() {
- mHasRootTask = true;
- StageCoordinator.this.onRootTaskAppeared();
- }
-
- @Override
- public void onChildTaskAppeared(int taskId) {
- StageCoordinator.this.onChildTaskAppeared(this, taskId);
- }
-
- @Override
- public void onStatusChanged(boolean visible, boolean hasChildren) {
- if (!mHasRootTask) return;
-
- if (mHasChildren != hasChildren) {
- mHasChildren = hasChildren;
- StageCoordinator.this.onStageHasChildrenChanged(this);
- }
- if (mVisible != visible) {
- mVisible = visible;
- StageCoordinator.this.onStageVisibilityChanged(this);
- }
- }
-
- @Override
- public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) {
- StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
- }
-
- @Override
- public void onRootTaskVanished() {
- reset();
- StageCoordinator.this.onRootTaskVanished();
- }
-
- @Override
- public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
- if (isSplitActive()) {
- final boolean isMainStage = mMainStageListener == this;
-
- // If visible, we preserve the app and keep it running. If an app becomes
- // unsupported in the bg, break split without putting anything on top
- boolean splitScreenVisible = isSplitScreenVisible();
- int stageType = STAGE_TYPE_UNDEFINED;
- if (splitScreenVisible) {
- stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
- }
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(stageType, wct);
- clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
- EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
- "app package " + taskInfo.baseIntent.getComponent()
- + " does not support splitscreen, or is a controlled activity"
- + " type"));
- if (splitScreenVisible) {
- handleUnsupportedSplitStart();
- }
- }
- }
-
- private void reset() {
- mHasRootTask = false;
- mVisible = false;
- mHasChildren = false;
- }
-
- public void dump(@NonNull PrintWriter pw, String prefix) {
- pw.println(prefix + "mHasRootTask=" + mHasRootTask);
- pw.println(prefix + "mVisible=" + mVisible);
- pw.println(prefix + "mHasChildren=" + mHasChildren);
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index d64c0a24be68..ae4bd1615ae1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -22,20 +22,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -74,20 +71,21 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
// stages should have this be set/being used
private boolean mIsActive;
-
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
- void onChildTaskAppeared(int taskId);
+ void onStageHasChildrenChanged(StageTaskListener stageTaskListener);
- void onStatusChanged(boolean visible, boolean hasChildren);
+ void onStageVisibilityChanged(StageTaskListener stageTaskListener);
- void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
+ void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present,
+ boolean visible);
void onRootTaskVanished();
- void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo);
+ void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
+ ActivityManager.RunningTaskInfo taskInfo);
}
private final Context mContext;
@@ -96,6 +94,12 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private final IconProvider mIconProvider;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
+ /** Whether or not the root task has been created. */
+ boolean mHasRootTask = false;
+ /** Whether or not the root task is visible. */
+ boolean mVisible = false;
+ /** Whether or not the root task has any children or not. */
+ boolean mHasChildren = false;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
protected SurfaceControl mDimLayer;
@@ -201,6 +205,7 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
mSplitDecorManager = new SplitDecorManager(
mRootTaskInfo.configuration,
mIconProvider);
+ mHasRootTask = true;
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
mSyncQueue.runInSync(t -> mDimLayer =
@@ -209,15 +214,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
mChildrenTaskInfo.put(taskId, taskInfo);
- mCallbacks.onChildTaskStatusChanged(taskId, true /* present */,
+ mCallbacks.onChildTaskStatusChanged(this, taskId, true /* present */,
taskInfo.isVisible && taskInfo.isVisibleRequested);
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
- mCallbacks.onChildTaskAppeared(taskId);
- sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -231,14 +229,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
taskInfo.taskId, taskInfo.baseActivity);
mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
if (mRootTaskInfo.taskId == taskInfo.taskId) {
- // Inflates split decor view only when the root task is visible.
- if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
- if (taskInfo.isVisible) {
- mSplitDecorManager.inflate(mContext, mRootLeash);
- } else {
- mSyncQueue.runInSync(t -> mSplitDecorManager.release(t));
- }
- }
mRootTaskInfo = taskInfo;
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
if (!taskInfo.supportsMultiWindow
@@ -250,25 +240,16 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
taskInfo.taskId);
// Leave split screen if the task no longer supports multi window or have
// uncontrolled task.
- mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
+ mCallbacks.onNoLongerSupportMultiWindow(this, taskInfo);
return;
}
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
- mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
+ mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
taskInfo.isVisible && taskInfo.isVisibleRequested);
- if (!ENABLE_SHELL_TRANSITIONS) {
- updateChildTaskSurface(
- taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
- }
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- sendStatusChanged();
}
@Override
@@ -278,6 +259,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
final int taskId = taskInfo.taskId;
mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo));
if (mRootTaskInfo.taskId == taskId) {
+ mHasRootTask = false;
+ mVisible = false;
+ mHasChildren = false;
mCallbacks.onRootTaskVanished();
mRootTaskInfo = null;
mRootLeash = null;
@@ -288,12 +272,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
} else if (mChildrenTaskInfo.contains(taskId)) {
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
- mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- sendStatusChanged();
+ mCallbacks.onChildTaskStatusChanged(this, taskId, false /* present */,
+ taskInfo.isVisible);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -455,26 +435,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
- private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash, boolean firstAppeared) {
- final Point taskPositionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- // The task surface might be released before running in the sync queue for the case like
- // trampoline launch, so check if the surface is valid before processing it.
- if (!leash.isValid()) {
- Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
- return;
- }
- t.setCrop(leash, null);
- t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
- if (firstAppeared) {
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- }
- });
- }
-
// ---------
// Previously only used in MainStage
boolean isActive() {
@@ -538,7 +498,19 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
private void sendStatusChanged() {
- mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
+ boolean hasChildren = mChildrenTaskInfo.size() > 0;
+ boolean visible = mRootTaskInfo.isVisible;
+ if (!mHasRootTask) return;
+
+ if (mHasChildren != hasChildren) {
+ mHasChildren = hasChildren;
+ mCallbacks.onStageHasChildrenChanged(this);
+ }
+
+ if (mVisible != visible) {
+ mVisible = visible;
+ mCallbacks.onStageVisibilityChanged(this);
+ }
}
@Override
@@ -554,5 +526,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
+ " baseActivity=" + taskInfo.baseActivity);
}
}
+ pw.println(prefix + "mHasRootTask=" + mHasRootTask);
+ pw.println(prefix + "mVisible=" + mVisible);
+ pw.println(prefix + "mHasChildren=" + mHasChildren);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index a2439a937512..5437167f58d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -61,6 +61,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
@@ -68,8 +69,6 @@ import static com.android.wm.shell.transition.TransitionAnimationHelper.isCovere
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -81,7 +80,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -90,12 +88,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
@@ -362,7 +358,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
final int flags = wallpaperTransit != WALLPAPER_TRANSITION_NONE
- && Flags.commonSurfaceAnimator()
? ScreenRotationAnimation.FLAG_HAS_WALLPAPER : 0;
startRotationAnimation(startTransaction, change, info, anim, flags,
animations, onAnimFinish);
@@ -823,72 +818,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return a;
}
- /** Builds an animator for the surface and adds it to the `animations` list. */
- static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull Animation anim, @NonNull SurfaceControl leash,
- @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
- @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
- @Nullable Rect clipRect, boolean isActivity) {
- if (Flags.commonSurfaceAnimator()) {
- DefaultSurfaceAnimator.buildSurfaceAnimation(animations, anim, leash, finishCallback,
- pool, mainExecutor, position, cornerRadius, clipRect, isActivity);
- return;
- }
- final SurfaceControl.Transaction transaction = pool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
- final Transformation transformation = new Transformation();
- final float[] matrix = new float[9];
- // Animation length is already expected to be scaled.
- va.overrideDurationScale(1.0f);
- va.setDuration(anim.computeDurationHint());
- final ValueAnimator.AnimatorUpdateListener updateListener = animation -> {
- final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
-
- applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
- position, cornerRadius, clipRect, isActivity);
- };
- va.addUpdateListener(updateListener);
-
- final Runnable finisher = () -> {
- applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
- position, cornerRadius, clipRect, isActivity);
-
- pool.release(transaction);
- mainExecutor.execute(() -> {
- animations.remove(va);
- finishCallback.run();
- });
- };
- va.addListener(new AnimatorListenerAdapter() {
- // It is possible for the end/cancel to be called more than once, which may cause
- // issues if the animating surface has already been released. Track the finished
- // state here to skip duplicate callbacks. See b/252872225.
- private boolean mFinished = false;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- onFinish();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- onFinish();
- }
-
- private void onFinish() {
- if (mFinished) return;
- mFinished = true;
- finisher.run();
- // The update listener can continue to be called after the animation has ended if
- // end() is called manually again before the finisher removes the animation.
- // Remove it manually here to prevent animating a released surface.
- // See b/252872225.
- va.removeUpdateListener(updateListener);
- }
- });
- animations.add(va);
- }
-
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
TransitionInfo.AnimationOptions options, float cornerRadius) {
@@ -1014,38 +943,4 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|| animType == ANIM_CLIP_REVEAL || animType == ANIM_OPEN_CROSS_PROFILE_APPS
|| animType == ANIM_FROM_STYLE;
}
-
- private static void applyTransformation(long time, SurfaceControl.Transaction t,
- SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
- Point position, float cornerRadius, @Nullable Rect immutableClipRect,
- boolean isActivity) {
- tmpTransformation.clear();
- anim.getTransformation(time, tmpTransformation);
- if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
- && anim.getExtensionEdges() != 0x0 && isActivity) {
- t.setEdgeExtensionEffect(leash, anim.getExtensionEdges());
- }
- if (position != null) {
- tmpTransformation.getMatrix().postTranslate(position.x, position.y);
- }
- t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
- t.setAlpha(leash, tmpTransformation.getAlpha());
-
- final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect);
- Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE);
- if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
- // Clip out any overflowing edge extension
- clipRect.inset(extensionInsets);
- t.setCrop(leash, clipRect);
- }
-
- if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
- // We can only apply rounded corner if a crop is set
- t.setCrop(leash, clipRect);
- t.setCornerRadius(leash, cornerRadius);
- }
-
- t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
- t.apply();
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 1a04997fa384..6f3aa11a8f52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -21,7 +21,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFA
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
-import static com.android.wm.shell.transition.DefaultTransitionHandler.buildSurfaceAnimation;
+import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
import static com.android.wm.shell.transition.Transitions.TAG;
import android.animation.Animator;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 230f7e6912ee..0bd3e083671e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -1,5 +1,6 @@
package com.android.wm.shell.desktopmode
+import android.animation.AnimatorTestRule
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -24,6 +25,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -38,6 +40,7 @@ import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
@@ -58,6 +61,9 @@ import org.mockito.quality.Strictness
@RunWithLooper
@RunWith(AndroidTestingRunner::class)
class DragToDesktopTransitionHandlerTest : ShellTestCase() {
+ @JvmField
+ @Rule
+ val mAnimatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var transitions: Transitions
@Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@@ -267,16 +273,36 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
}
@Test
- fun cancelDragToDesktop_startWasReady_cancel() {
- startDrag(defaultHandler)
+ fun cancelDragToDesktop_startWasReady_cancel_merged() {
+ val startToken = startDrag(defaultHandler)
// Then user cancelled after it had already started.
- defaultHandler.cancelDragToDesktopTransition(
- DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
- )
+ val cancelToken = cancelDragToDesktopTransition(
+ defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+ defaultHandler.mergeAnimation(
+ cancelToken,
+ TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
+ mock<SurfaceControl.Transaction>(),
+ startToken,
+ mock<Transitions.TransitionFinishCallback>())
+
+ // Cancel animation should run since it had already started.
+ verify(dragAnimator).cancelAnimator()
+ assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
+ }
+
+ @Test
+ fun cancelDragToDesktop_startWasReady_cancel_aborted() {
+ val startToken = startDrag(defaultHandler)
+
+ // Then user cancelled after it had already started.
+ val cancelToken = cancelDragToDesktopTransition(
+ defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+ defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null)
// Cancel animation should run since it had already started.
verify(dragAnimator).cancelAnimator()
+ assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
}
@Test
@@ -585,6 +611,23 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
return token
}
+ private fun cancelDragToDesktopTransition(
+ handler: DragToDesktopTransitionHandler,
+ cancelState: DragToDesktopTransitionHandler.CancelState): IBinder {
+ val token = mock<IBinder>()
+ whenever(
+ transitions.startTransition(
+ eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ )
+ .thenReturn(token)
+ handler.cancelDragToDesktopTransition(cancelState)
+ mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ return token
+ }
+
private fun performEarlyCancel(
handler: DragToDesktopTransitionHandler,
cancelState: DragToDesktopTransitionHandler.CancelState
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index b7b7d0d35bcf..189684dc391a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -23,10 +23,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
@@ -64,8 +63,6 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests extends ShellTestCase {
- private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
@Mock
private ShellTaskOrganizer mTaskOrganizer;
@@ -117,20 +114,20 @@ public final class StageTaskListenerTests extends ShellTestCase {
public void testRootTaskAppeared() {
assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
verify(mCallbacks).onRootTaskAppeared();
- verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(false));
+ verify(mCallbacks, never()).onStageHasChildrenChanged(mStageTaskListener);
+ verify(mCallbacks, never()).onStageVisibilityChanged(mStageTaskListener);
}
@Test
- public void testChildTaskAppeared() {
- // With shell transitions, the transition manages status changes, so skip this test.
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo childTask =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+ public void testRootTaskVisible() {
+ mStageTaskListener.onTaskVanished(mRootTask);
+ mRootTask = new TestRunningTaskInfoBuilder().setVisible(true).build();
+ mRootTask.parentTaskId = INVALID_TASK_ID;
+ mSurfaceControl = new SurfaceControl.Builder().setName("test").build();
+ mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl);
- mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl);
+ verify(mCallbacks).onStageVisibilityChanged(mStageTaskListener);
- assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue();
- verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
@Test(expected = IllegalArgumentException.class)
@@ -140,29 +137,13 @@ public final class StageTaskListenerTests extends ShellTestCase {
}
@Test
- public void testTaskVanished() {
- // With shell transitions, the transition manages status changes, so skip this test.
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo childTask =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
- mStageTaskListener.mRootTaskInfo = mRootTask;
- mStageTaskListener.mChildrenTaskInfo.put(childTask.taskId, childTask);
-
- mStageTaskListener.onTaskVanished(childTask);
- verify(mCallbacks, times(2)).onStatusChanged(eq(mRootTask.isVisible), eq(false));
-
- mStageTaskListener.onTaskVanished(mRootTask);
- verify(mCallbacks).onRootTaskVanished();
- }
-
- @Test
public void testTaskInfoChanged_notSupportsMultiWindow() {
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
childTask.supportsMultiWindow = false;
mStageTaskListener.onTaskInfoChanged(childTask);
- verify(mCallbacks).onNoLongerSupportMultiWindow(childTask);
+ verify(mCallbacks).onNoLongerSupportMultiWindow(mStageTaskListener, childTask);
}
@Test
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 4d6ddfdaef2e..3cd5f5266ef2 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -18,7 +18,9 @@ package android.media;
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
+import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -291,12 +293,24 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public static final int MUTED_BY_VOLUME_SHAPER = (1 << 5);
+ /**
+ * @hide
+ * Flag used when muted by the track's port volume.
+ *
+ * <p>Note: this will replace the stream volume mute when using the AudioFlinger port volume
+ * APIs
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int MUTED_BY_PORT_VOLUME = (1 << 6);
/** @hide */
@IntDef(
flag = true,
value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED,
- MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER})
+ MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER,
+ MUTED_BY_PORT_VOLUME})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerMuteEvent {
}
@@ -858,6 +872,9 @@ public final class AudioPlaybackConfiguration implements Parcelable {
if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) {
apcToString.append("volumeShaper ");
}
+ if ((mMutedState & MUTED_BY_PORT_VOLUME) != 0) {
+ apcToString.append("portVolume ");
+ }
}
apcToString.append(" ").append(mFormatInfo);
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a8b863bc67f9..bf09cb07a8ed 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1698,12 +1698,12 @@ public class AudioSystem
}
/** @hide Wrapper for native methods called from AudioService */
- public static int setStreamVolumeIndexAS(int stream, int index, int device) {
+ public static int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
if (DEBUG_VOLUME) {
Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
- + " dev=" + Integer.toHexString(device) + " idx=" + index);
+ + " dev=" + Integer.toHexString(device) + " idx=" + index + " muted=" + muted);
}
- return setStreamVolumeIndex(stream, index, device);
+ return setStreamVolumeIndex(stream, index, muted, device);
}
// usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
@@ -1774,7 +1774,8 @@ public class AudioSystem
@UnsupportedAppUsage
public static native int initStreamVolume(int stream, int indexMin, int indexMax);
@UnsupportedAppUsage
- private static native int setStreamVolumeIndex(int stream, int index, int device);
+ private static native int setStreamVolumeIndex(int stream, int index, boolean muted,
+ int device);
/** @hide */
public static native int getStreamVolumeIndex(int stream, int device);
/**
@@ -1787,7 +1788,7 @@ public class AudioSystem
* @return command completion status.
*/
public static native int setVolumeIndexForAttributes(@NonNull AudioAttributes attributes,
- int index, int device);
+ int index, boolean muted, int device);
/**
* @hide
* get the volume index for the given {@link AudioAttributes}.
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 8c5d8778c96f..8fb16d8ac840 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -78,6 +78,10 @@ private constructor(private val context: Context, private val request: GetPrefer
for (activityClass in request.activityClasses) {
add(activityClass)
}
+ // Temporarily add all screens
+ for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
+ addPreferenceScreenFromRegistry(key, Activity::class.java)
+ }
}
fun build() = builder.build()
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index 48798da57dae..6646d6c32d15 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -34,7 +34,7 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
ImmutableMap.of()
}
- private val preferenceScreens: PreferenceScreenMap
+ val preferenceScreens: PreferenceScreenMap
get() = preferenceScreensSupplier.get()
private var readWritePermitProvider: ReadWritePermitProvider? = null
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index b69912a3fd36..73d0beccd0ba 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0"
+ extra["jetpackComposeVersion"] = "1.7.3"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 8f8275bed702..914f06c1fef7 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,7 +54,7 @@ android {
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.3.0")
+ api("androidx.compose.material3:material3:1.4.0-alpha01")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 2c55779c9a01..693fb3541bb8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -46,6 +46,7 @@ import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.TopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
@@ -326,6 +327,9 @@ private fun TwoRowsTopAppBar(
// Sets the app bar's height offset limit to hide just the bottom title area and keep top title
// visible when collapsed.
scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx.floatValue
+ if (isSpaExpressiveEnabled) {
+ LaunchedEffect(scrollBehavior?.state?.heightOffsetLimit) { scrollBehavior?.collapse() }
+ }
// Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the
// bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index 7d8ee79b3344..60b1e639a2b7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -39,7 +39,6 @@ import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.compose.horizontalValues
import com.android.settingslib.spa.framework.compose.verticalValues
import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
import com.android.settingslib.spa.framework.theme.settingsBackground
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -56,9 +55,6 @@ fun SettingsScaffold(
) {
ActivityTitle(title)
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
- if (isSpaExpressiveEnabled) {
- LaunchedEffect(scrollBehavior.state.heightOffsetLimit) { scrollBehavior.collapse() }
- }
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 1a99d25786ff..65b22758946d 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -39,6 +39,7 @@ android_library {
"configinfra_framework_flags_java_lib",
"device_config_service_flags_java",
"libaconfig_java_proto_lite",
+ "notification_flags_lib",
"SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayUtils",
],
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 6c3183191163..ebeee8564d2f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -29,6 +29,7 @@ import android.hardware.display.ColorDisplayManager;
import android.icu.util.ULocale;
import android.media.AudioManager;
import android.media.RingtoneManager;
+import android.media.Utils;
import android.net.Uri;
import android.os.LocaleList;
import android.os.RemoteException;
@@ -309,6 +310,13 @@ public class SettingsHelper {
return SILENT_RINGTONE;
}
} else {
+ // If the ringtone/notification support the vibration, use the original value.
+ final int ringtoneType = getRingtoneType(name);
+ if ((ringtoneType == RingtoneManager.TYPE_RINGTONE
+ || ringtoneType == RingtoneManager.TYPE_NOTIFICATION)
+ && hasVibrationSettings(value, ringtoneType)) {
+ return value;
+ }
return getCanonicalRingtoneValue(value);
}
}
@@ -362,6 +370,15 @@ public class SettingsHelper {
return;
}
+ // If the ringtone/notification has vibration, we backup original value in onBackupValue.
+ // So use the value directly for restoring.
+ if ((ringtoneType == RingtoneManager.TYPE_RINGTONE
+ || ringtoneType == RingtoneManager.TYPE_NOTIFICATION)
+ && hasVibrationSettings(value, ringtoneType)) {
+ RingtoneManager.setActualDefaultRingtoneUri(mContext, ringtoneType, Uri.parse(value));
+ return;
+ }
+
Uri ringtoneUri = null;
try {
ringtoneUri =
@@ -617,6 +634,19 @@ public class SettingsHelper {
return allLocales.remove(toFullLocale(filteredLocale));
}
+ private boolean hasVibrationSettings(String value, int type) {
+ if (Utils.hasVibration(Uri.parse(value)) && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)) {
+ if (type == RingtoneManager.TYPE_RINGTONE) {
+ return android.media.audio.Flags.enableRingtoneHapticsCustomization();
+ }
+ if (type == RingtoneManager.TYPE_NOTIFICATION) {
+ return com.android.server.notification.Flags.notificationVibrationInSoundUri();
+ }
+ }
+ return false;
+ }
+
/**
* Sets the locale specified. Input data is the byte representation of comma separated
* multiple BCP-47 language tags. For backwards compatibility, strings of the form
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 4b10b56f49fb..cea2bbc5c535 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -37,9 +37,12 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.media.AudioManager;
+import android.media.Utils;
import android.net.Uri;
import android.os.Bundle;
import android.os.LocaleList;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -54,8 +57,11 @@ import com.android.internal.R;
import org.junit.After;
import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -63,6 +69,7 @@ import org.mockito.MockitoAnnotations;
* Tests for the SettingsHelperTest
*/
@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SettingsHelperTest {
private static final String SETTING_KEY = "setting_key";
private static final String SETTING_VALUE = "setting_value";
@@ -74,9 +81,13 @@ public class SettingsHelperTest {
"content://media/internal/audio/media/20?title=DefaultNotification&canonical=1";
private static final String DEFAULT_ALARM_VALUE =
"content://media/internal/audio/media/30?title=DefaultAlarm&canonical=1";
+ private static final String VIBRATION_FILE_NAME = "haptics.xml";
private SettingsHelper mSettingsHelper;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private AudioManager mAudioManager;
@@ -120,6 +131,22 @@ public class SettingsHelperTest {
}
@Test
+ @EnableFlags({android.media.audio.Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION,
+ com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI})
+ public void testOnBackupValue_ringtoneVibrationSupport_returnsSameValue() {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)).thenReturn(
+ true);
+ String testRingtoneVibrationValue = createUriWithVibration(DEFAULT_RINGTONE_VALUE);
+ String testNotificationVibrationValue = createUriWithVibration(DEFAULT_NOTIFICATION_VALUE);
+
+ assertEquals(testRingtoneVibrationValue, mSettingsHelper.onBackupValue(
+ Settings.System.RINGTONE, testRingtoneVibrationValue));
+ assertEquals(testNotificationVibrationValue, mSettingsHelper.onBackupValue(
+ Settings.System.NOTIFICATION_SOUND, testNotificationVibrationValue));
+ }
+
+ @Test
public void testGetRealValue_settingNotReplaced_returnsSameValue() {
when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(false);
@@ -675,6 +702,30 @@ public class SettingsHelperTest {
.isEqualTo(null);
}
+ @Test
+ @EnableFlags({android.media.audio.Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION,
+ com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI})
+ public void testRestoreValue_ringtoneVibrationSupport_restoreValue() {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)).thenReturn(
+ true);
+ String testRingtoneVibrationValue = createUriWithVibration(DEFAULT_RINGTONE_VALUE);
+ String testNotificationVibrationValue = createUriWithVibration(DEFAULT_NOTIFICATION_VALUE);
+ ContentProvider mockMediaContentProvider =
+ new MockContentProvider(mContext) {
+ @Override
+ public String getType(Uri url) {
+ return "audio/ogg";
+ }
+ };
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ resetRingtoneSettingsToDefault();
+
+ assertRingtoneSettingsRestoring(Settings.System.RINGTONE, testRingtoneVibrationValue);
+ assertRingtoneSettingsRestoring(
+ Settings.System.NOTIFICATION_SOUND, testNotificationVibrationValue);
+ }
+
private static class MockSettingsProvider extends MockContentProvider {
private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>();
MockSettingsProvider(Context context) {
@@ -766,4 +817,25 @@ public class SettingsHelperTest {
assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
.isEqualTo(DEFAULT_ALARM_VALUE);
}
+
+ private String createUriWithVibration(String defaultUriString) {
+ return Uri.parse(defaultUriString).buildUpon()
+ .appendQueryParameter(
+ Utils.VIBRATION_URI_PARAM, VIBRATION_FILE_NAME).build().toString();
+ }
+
+ private void assertRingtoneSettingsRestoring(
+ String settings, String testRingtoneSettingsValue) {
+ mSettingsHelper.restoreValue(
+ mContext,
+ mContentResolver,
+ new ContentValues(),
+ Uri.EMPTY,
+ settings,
+ testRingtoneSettingsValue,
+ 0);
+
+ assertThat(Settings.System.getString(mContentResolver, settings))
+ .isEqualTo(testRingtoneSettingsValue);
+ }
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a21a80506279..189294484085 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1481,3 +1481,9 @@ flag {
bug: "370863642"
}
+flag {
+ name: "notes_role_qs_tile"
+ namespace: "systemui"
+ description: "Enables notes role qs tile which opens default notes role app in app bubbles"
+ bug: "357863750"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 93a99bd948f1..18f40c98fe04 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -136,20 +136,16 @@ constructor(
)
/**
- * The timings when animating a View into an app using a spring animator.
- *
- * Important: since springs don't have fixed durations, these timings represent fractions of
- * the progress between the spring's initial value and its final value.
- *
- * TODO(b/372858592): make this a separate class explicitly using percentages.
+ * The timings when animating a View into an app using a spring animator. These timings
+ * represent fractions of the progress between the spring's initial value and its final
+ * value.
*/
val SPRING_TIMINGS =
- TransitionAnimator.Timings(
- totalDuration = 1000L,
- contentBeforeFadeOutDelay = 0L,
- contentBeforeFadeOutDuration = 800L,
- contentAfterFadeInDelay = 850L,
- contentAfterFadeInDuration = 135L,
+ TransitionAnimator.SpringTimings(
+ contentBeforeFadeOutDelay = 0f,
+ contentBeforeFadeOutDuration = 0.8f,
+ contentAfterFadeInDelay = 0.85f,
+ contentAfterFadeInDuration = 0.135f,
)
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 1d8ff77ac719..9dc93484a638 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -52,7 +52,7 @@ class TransitionAnimator(
private val interpolators: Interpolators,
/** [springTimings] and [springInterpolators] must either both be null or both not null. */
- private val springTimings: Timings? = null,
+ private val springTimings: SpringTimings? = null,
private val springInterpolators: Interpolators? = null,
private val springParams: SpringParams = DEFAULT_SPRING_PARAMS,
) {
@@ -83,8 +83,22 @@ class TransitionAnimator(
delay: Long,
duration: Long,
): Float {
+ return getProgressInternal(
+ timings.totalDuration.toFloat(),
+ linearProgress,
+ delay.toFloat(),
+ duration.toFloat(),
+ )
+ }
+
+ private fun getProgressInternal(
+ totalDuration: Float,
+ linearProgress: Float,
+ delay: Float,
+ duration: Float,
+ ): Float {
return MathUtils.constrain(
- (linearProgress * timings.totalDuration - delay) / duration,
+ (linearProgress * totalDuration - delay) / duration,
0.0f,
1.0f,
)
@@ -367,6 +381,25 @@ class TransitionAnimator(
val contentAfterFadeInDuration: Long,
)
+ /**
+ * The timings (durations and delays) used by the multi-spring animator. These are expressed as
+ * fractions of 1, similar to how the progress of an animator can be expressed as a float value
+ * between 0 and 1.
+ */
+ class SpringTimings(
+ /** The portion of animation to wait before fading out the expanding content. */
+ val contentBeforeFadeOutDelay: Float,
+
+ /** The portion of animation during which the expanding content fades out. */
+ val contentBeforeFadeOutDuration: Float,
+
+ /** The portion of animation to wait before fading in the expanded content. */
+ val contentAfterFadeInDelay: Float,
+
+ /** The portion of animation during which the expanded content fades in. */
+ val contentAfterFadeInDuration: Float,
+ )
+
/** The interpolators used by this animator. */
data class Interpolators(
/** The interpolator used for the Y position, width, height and corner radius. */
@@ -576,18 +609,14 @@ class TransitionAnimator(
}
override fun onAnimationEnd(animation: Animator) {
- if (DEBUG) {
- Log.d(TAG, "Animation ended")
- }
-
- // TODO(b/330672236): Post this to the main thread instead so that it does not
- // flicker with Flexiglass enabled.
- controller.onTransitionAnimationEnd(isExpandingFullyAbove)
- transitionContainerOverlay.remove(windowBackgroundLayer)
-
- if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
- openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
- }
+ onAnimationEnd(
+ controller,
+ isExpandingFullyAbove,
+ windowBackgroundLayer,
+ transitionContainerOverlay,
+ openingWindowSyncViewOverlay,
+ moveBackgroundLayerWhenAppVisibilityChanges,
+ )
}
}
)
@@ -1021,34 +1050,47 @@ class TransitionAnimator(
cornerRadii[7] = state.bottomCornerRadius
drawable.cornerRadii = cornerRadii
- val timings: Timings
val interpolators: Interpolators
+ val fadeInProgress: Float
+ val fadeOutProgress: Float
if (useSpring) {
- timings = springTimings!!
interpolators = springInterpolators!!
+ val timings = springTimings!!
+ fadeInProgress =
+ getProgressInternal(
+ totalDuration = 1f,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration,
+ )
+ fadeOutProgress =
+ getProgressInternal(
+ totalDuration = 1f,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration,
+ )
} else {
- timings = this.timings
interpolators = this.interpolators
+ fadeInProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration,
+ )
+ fadeOutProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration,
+ )
}
// We first fade in the background layer to hide the expanding view, then fade it out with
// SRC mode to draw a hole punch in the status bar and reveal the opening window (if
// needed). If !isLaunching, the reverse happens.
- val fadeInProgress =
- getProgress(
- timings,
- linearProgress,
- timings.contentBeforeFadeOutDelay,
- timings.contentBeforeFadeOutDuration,
- )
- val fadeOutProgress =
- getProgress(
- timings,
- linearProgress,
- timings.contentAfterFadeInDelay,
- timings.contentAfterFadeInDuration,
- )
-
if (isLaunching) {
if (fadeInProgress < 1) {
val alpha =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index a525f36c71ce..9390664d1283 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -30,14 +30,10 @@ import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.keyguard.LockIconView
-import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -61,7 +57,6 @@ constructor(
private val windowManager: WindowManager,
private val authController: AuthController,
private val featureFlags: FeatureFlagsClassic,
- private val lockIconViewController: Lazy<LockIconViewController>,
private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
@@ -71,42 +66,28 @@ constructor(
) {
@Composable
fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
- if (!KeyguardBottomAreaRefactor.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled) {
- return
- }
-
val context = LocalContext.current
AndroidView(
factory = { context ->
- val view =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(
- context,
- null,
- logger = LongPressHandlingViewLogger(logBuffer, tag = TAG)
- )
- .apply {
- id = R.id.device_entry_icon_view
- DeviceEntryIconViewBinder.bind(
- applicationScope,
- this,
- deviceEntryIconViewModel.get(),
- deviceEntryForegroundViewModel.get(),
- deviceEntryBackgroundViewModel.get(),
- falsingManager.get(),
- vibratorHelper.get(),
- overrideColor,
- )
- }
- } else {
- // KeyguardBottomAreaRefactor.isEnabled
- LockIconView(context, null).apply {
- id = R.id.lock_icon_view
- lockIconViewController.get().setLockIconView(this)
- }
+ DeviceEntryIconView(
+ context,
+ null,
+ logger = LongPressHandlingViewLogger(logBuffer, tag = TAG),
+ )
+ .apply {
+ id = R.id.device_entry_icon_view
+ DeviceEntryIconViewBinder.bind(
+ applicationScope,
+ this,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ overrideColor,
+ )
}
- view
},
modifier =
modifier.element(LockIconElementKey).layout { measurable, _ ->
@@ -141,9 +122,7 @@ constructor(
* On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are
* the same as the bounds of the sensor.
*/
- private fun lockIconBounds(
- context: Context,
- ): IntRect {
+ private fun lockIconBounds(context: Context): IntRect {
val windowViewBounds = windowManager.currentWindowMetrics.bounds
var widthPx = windowViewBounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
@@ -162,10 +141,7 @@ constructor(
val (center, radius) =
if (authController.isUdfpsSupported && udfpsLocation != null) {
Pair(
- IntOffset(
- x = udfpsLocation.x,
- y = udfpsLocation.y,
- ),
+ IntOffset(x = udfpsLocation.x, y = udfpsLocation.y),
authController.udfpsRadius.toInt(),
)
} else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 2e39524baaad..73c4fab7b646 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
-import com.android.systemui.customization.R as customizationR
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
@@ -57,12 +57,12 @@ constructor(
Row(
modifier =
Modifier.padding(
- horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ horizontal = dimensionResource(customR.dimen.clock_padding_start)
)
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
WeatherElement(
- weatherClockElementViewId = customizationR.id.weather_clock_time,
+ weatherClockElementViewId = customR.id.weather_clock_time,
clock = clock,
elementKey = WeatherClockElementKeys.timeElementKey,
)
@@ -75,7 +75,7 @@ constructor(
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = customizationR.id.weather_clock_date,
+ weatherClockElementViewId = customR.id.weather_clock_date,
clock = clock,
elementKey = WeatherClockElementKeys.dateElementKey,
modifier = modifier,
@@ -88,7 +88,7 @@ constructor(
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = customizationR.id.weather_clock_weather_icon,
+ weatherClockElementViewId = customR.id.weather_clock_weather_icon,
clock = clock,
elementKey = WeatherClockElementKeys.weatherIconElementKey,
modifier = modifier.wrapContentSize(),
@@ -101,7 +101,7 @@ constructor(
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = customizationR.id.weather_clock_alarm_dnd,
+ weatherClockElementViewId = customR.id.weather_clock_alarm_dnd,
clock = clock,
elementKey = WeatherClockElementKeys.dndAlarmElementKey,
modifier = modifier.wrapContentSize(),
@@ -114,7 +114,7 @@ constructor(
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = customizationR.id.weather_clock_temperature,
+ weatherClockElementViewId = customR.id.weather_clock_temperature,
clock = clock,
elementKey = WeatherClockElementKeys.temperatureElementKey,
modifier = modifier.wrapContentSize(),
@@ -159,7 +159,7 @@ constructor(
modifier =
Modifier.height(IntrinsicSize.Max)
.padding(
- horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ horizontal = dimensionResource(customR.dimen.clock_padding_start)
)
.burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
) {
@@ -168,7 +168,7 @@ constructor(
modifier =
Modifier.fillMaxSize()
.padding(
- start = dimensionResource(customizationR.dimen.clock_padding_start)
+ start = dimensionResource(customR.dimen.clock_padding_start)
)
) {
Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index 8b9e9274b448..e4c60e166fd5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,6 +18,8 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
/**
@@ -44,7 +46,7 @@ fun NotificationScrimNestedScrollConnection(
orientation = Orientation.Vertical,
// scrolling up and inner content is taller than the scrim, so scrim needs to
// expand; content can scroll once scrim is at the minScrimOffset.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0 &&
offsetBeforeStart == 0f &&
contentHeight() > minVisibleScrimHeight() &&
@@ -52,36 +54,38 @@ fun NotificationScrimNestedScrollConnection(
},
// scrolling down and content is done scrolling to top. After that, the scrim
// needs to collapse; collapse the scrim until it is at the maxScrimOffset.
- canStartPostScroll = { offsetAvailable, _ ->
+ canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
},
canStartPostFling = { false },
- canContinueScroll = {
- val currentHeight = scrimOffset()
- minScrimOffset() < currentHeight && currentHeight < maxScrimOffset
- },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val currentHeight = scrimOffset()
val amountConsumed =
if (offsetAvailable > 0) {
val amountLeft = maxScrimOffset - currentHeight
- offsetAvailable.coerceAtMost(amountLeft)
+ offsetAvailable.fastCoerceAtMost(amountLeft)
} else {
val amountLeft = minScrimOffset() - currentHeight
- offsetAvailable.coerceAtLeast(amountLeft)
+ offsetAvailable.fastCoerceAtLeast(amountLeft)
}
snapScrimOffset(currentHeight + amountConsumed)
amountConsumed
},
- // Don't consume the velocity on pre/post fling
onStop = { velocityAvailable ->
onStop(velocityAvailable)
if (scrimOffset() < minScrimOffset()) {
animateScrimOffset(minScrimOffset())
}
- { 0f }
+ // Don't consume the velocity on pre/post fling
+ 0f
+ },
+ onCancel = {
+ onStop(0f)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
},
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index a706585deebc..edb05ebd77d1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.max
import kotlin.math.roundToInt
@@ -86,21 +87,25 @@ fun NotificationStackNestedScrollConnection(
): PriorityNestedScrollConnection {
return PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
- canStartPreScroll = { _, _ -> false },
- canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { _, _, _ -> false },
+ canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canContinueScroll = { stackOffset() > 0f },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable ->
- onScroll(offsetAvailable)
- offsetAvailable
+ onScroll = { offsetAvailable, _ ->
+ val minOffset = 0f
+ val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
+ if (consumed != 0f) {
+ onScroll(consumed)
+ }
+ consumed
},
onStop = { velocityAvailable ->
onStop(velocityAvailable)
- suspend { velocityAvailable }
+ velocityAvailable
},
+ onCancel = { onStop(0f) },
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 085157ac72b9..7e288ddd3a4c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -27,9 +27,10 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
-import com.android.compose.nestedscroll.SuspendedValue
import kotlin.math.absoluteValue
+internal typealias SuspendedValue<T> = suspend () -> T
+
internal interface DraggableHandler {
/**
* Start a drag in the given [startedPosition], with the given [overSlop] and number of
@@ -612,7 +613,7 @@ internal class NestedScrollHandlerImpl(
return PriorityNestedScrollConnection(
orientation = orientation,
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
canChangeScene =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
@@ -638,7 +639,7 @@ internal class NestedScrollHandlerImpl(
isIntercepting = true
true
},
- canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
val behavior: NestedScrollBehavior =
when {
offsetAvailable > 0f -> topOrLeftBehavior
@@ -693,8 +694,7 @@ internal class NestedScrollHandlerImpl(
canStart
},
- canContinueScroll = { true },
- canScrollOnFling = false,
+ canStopOnPreFling = { true },
onStart = { offsetAvailable ->
val pointersInfo = pointersInfo()
dragController =
@@ -704,7 +704,7 @@ internal class NestedScrollHandlerImpl(
overSlop = if (isIntercepting) 0f else offsetAvailable,
)
},
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val controller = dragController ?: error("Should be called after onStart")
// TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
@@ -713,10 +713,18 @@ internal class NestedScrollHandlerImpl(
},
onStop = { velocityAvailable ->
val controller = dragController ?: error("Should be called after onStart")
-
- controller
- .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
- .also { dragController = null }
+ try {
+ controller
+ .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
+ .invoke()
+ } finally {
+ dragController = null
+ }
+ },
+ onCancel = {
+ val controller = dragController ?: error("Should be called after onStart")
+ controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
+ dragController = null
},
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 2657d7cf8156..3c3c612c028b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -430,6 +430,10 @@ internal class MutableSceneTransitionLayoutStateImpl(
// Replace the transition.
transitionStates =
transitionStates.subList(0, transitionStates.lastIndex) + transition
+
+ // Make sure it is removed from the finishedTransitions set if it was already
+ // finished.
+ finishedTransitions.remove(currentState)
} else {
// Append the new transition.
transitionStates = transitionStates + transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 205267da151a..f0043e1e89b0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
-import com.android.compose.nestedscroll.SuspendedValue
import kotlin.math.absoluteValue
import kotlinx.coroutines.CompletableDeferred
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index 4ae323517b26..ecf64b771d1f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,8 @@ package com.android.compose.nestedscroll
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
/**
* A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -43,35 +45,32 @@ fun LargeTopAppBarNestedScrollConnection(
orientation = Orientation.Vertical,
// When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
// expand. Then, you can then scroll down the content.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight()
},
// When swiping down, the content will scroll up until it reaches the top. Then, the
// LargeTopAppBar will expand until it reaches its [maxHeight].
- canStartPostScroll = { offsetAvailable, _ ->
+ canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && height() < maxHeight()
},
canStartPostFling = { false },
- canContinueScroll = {
- val currentHeight = height()
- minHeight() < currentHeight && currentHeight < maxHeight()
- },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { /* do nothing */ },
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val currentHeight = height()
val amountConsumed =
if (offsetAvailable > 0) {
val amountLeft = maxHeight() - currentHeight
- offsetAvailable.coerceAtMost(amountLeft)
+ offsetAvailable.fastCoerceAtMost(amountLeft)
} else {
val amountLeft = minHeight() - currentHeight
- offsetAvailable.coerceAtLeast(amountLeft)
+ offsetAvailable.fastCoerceAtLeast(amountLeft)
}
onHeightChanged(currentHeight + amountConsumed)
amountConsumed
},
// Don't consume the velocity on pre/post fling
- onStop = { { 0f } },
+ onStop = { 0f },
+ onCancel = { /* do nothing */ },
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index a3641e6635e7..636c55799ff2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,37 +16,59 @@
package com.android.compose.nestedscroll
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import com.android.compose.ui.util.SpaceVectorConverter
+import kotlin.math.abs
import kotlin.math.sign
-
-internal typealias SuspendedValue<T> = suspend () -> T
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
/**
- * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and
- * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling.
- * If it does, it will scroll before its children, until [canContinueScroll] allows it.
+ * A [NestedScrollConnection] that intercepts scroll events in priority mode.
*
- * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop]
- * after [onStart].
+ * Priority mode allows this connection to take control over scroll events within a nested scroll
+ * hierarchy. When in priority mode, this connection consumes scroll events before its children,
+ * enabling custom scrolling behaviors like sticky headers.
*
+ * @param orientation The orientation of the scroll.
+ * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
+ * events in pre-scroll mode.
+ * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
+ * events in post-scroll mode.
+ * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
+ * events in post-fling mode.
+ * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
+ * events in pre-fling (i.e. as soon as the user lifts their fingers).
+ * @param onStart lambda that is called when the connection starts consuming scroll events.
+ * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
+ * consumed amount.
+ * @param onStop lambda that is called when the connection stops consuming scroll events and returns
+ * the consumed velocity.
+ * @param onCancel lambda that is called when the connection is cancelled.
* @sample LargeTopAppBarNestedScrollConnection
* @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
*/
class PriorityNestedScrollConnection(
orientation: Orientation,
- private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
- private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+ private val canStartPreScroll:
+ (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
+ private val canStartPostScroll:
+ (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
- private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
- private val canScrollOnFling: Boolean,
+ private val canStopOnPreFling: () -> Boolean,
private val onStart: (offsetAvailable: Float) -> Unit,
- private val onScroll: (offsetAvailable: Float) -> Float,
- private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>,
+ private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
+ private val onStop: suspend (velocityAvailable: Float) -> Float,
+ private val onCancel: () -> Unit,
) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
/** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
@@ -54,6 +76,9 @@ class PriorityNestedScrollConnection(
private var offsetScrolledBeforePriorityMode = 0f
+ /** This job allows us to interrupt the onStop animation */
+ private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+
override fun onPostScroll(
consumed: Offset,
available: Offset,
@@ -64,62 +89,48 @@ class PriorityNestedScrollConnection(
// the beginning or from the last fling gesture.
val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
- if (
- isPriorityMode ||
- (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
- !canStartPostScroll(availableFloat, offsetBeforeStart)
- ) {
+ if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
// The priority mode cannot start so we won't consume the available offset.
return Offset.Zero
}
- return onPriorityStart(availableFloat).toOffset()
+ return start(availableFloat, source).toOffset()
}
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (!isPriorityMode) {
- if (source == NestedScrollSource.UserInput || canScrollOnFling) {
- val availableFloat = available.toFloat()
- if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) {
- return onPriorityStart(availableFloat).toOffset()
- }
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += availableFloat
+ val availableFloat = available.toFloat()
+ if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+ return start(availableFloat, source).toOffset()
}
-
- return Offset.Zero
- }
-
- val availableFloat = available.toFloat()
- if (!canContinueScroll(source)) {
- // Step 3a: We have lost priority and we no longer need to intercept scroll events.
- onPriorityStop(velocity = 0f)
-
- // We've just reset offsetScrolledBeforePriorityMode to 0f
// We want to track the amount of offset consumed before entering priority mode
offsetScrolledBeforePriorityMode += availableFloat
-
return Offset.Zero
}
- // Step 2: We have the priority and can consume the scroll events.
- return onScroll(availableFloat).toOffset()
+ return scroll(available.toFloat(), source).toOffset()
}
override suspend fun onPreFling(available: Velocity): Velocity {
- if (isPriorityMode && canScrollOnFling) {
- // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ if (!isPriorityMode) {
+ resetOffsetTracker()
return Velocity.Zero
}
- // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
- // of the fling gesture.
- return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity()
+
+ if (canStopOnPreFling()) {
+ // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
+ // velocity of the fling gesture.
+ return stop(velocityAvailable = available.toFloat()).toVelocity()
+ }
+
+ // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val availableFloat = available.toFloat()
if (isPriorityMode) {
- return onPriorityStop(velocity = availableFloat).invoke().toVelocity()
+ return stop(velocityAvailable = availableFloat).toVelocity()
}
if (!canStartPostFling(availableFloat)) {
@@ -131,10 +142,14 @@ class PriorityNestedScrollConnection(
// TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
// overscroll behavior on the Scene level.
val smallOffset = availableFloat.sign
- onPriorityStart(availableOffset = smallOffset)
+ start(
+ availableOffset = smallOffset,
+ source = NestedScrollSource.SideEffect,
+ skipScroll = true,
+ )
// This is the last event of a scroll gesture.
- return onPriorityStop(availableFloat).invoke().toVelocity()
+ return stop(availableFloat).toVelocity()
}
/**
@@ -143,36 +158,76 @@ class PriorityNestedScrollConnection(
* TODO(b/303224944) This method should be removed.
*/
fun reset() {
- // Step 3c: To ensure that an onStop is always called for every onStart.
- onPriorityStop(velocity = 0f)
+ if (isPriorityMode) {
+ // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+ cancel()
+ } else {
+ resetOffsetTracker()
+ }
}
- private fun onPriorityStart(availableOffset: Float): Float {
- if (isPriorityMode) {
- error("This should never happen, onPriorityStart() was called when isPriorityMode")
+ private fun shouldStop(consumed: Float): Boolean {
+ return consumed == 0f
+ }
+
+ private fun start(
+ availableOffset: Float,
+ source: NestedScrollSource,
+ skipScroll: Boolean = false,
+ ): Float {
+ check(!isPriorityMode) {
+ "This should never happen, start() was called when isPriorityMode"
}
// Step 1: It's our turn! We start capturing scroll events when one of our children has an
// available offset following a scroll event.
isPriorityMode = true
+ onStopJob.cancel()
+
// Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
// lifted (step 3b), or this object has been destroyed (step 3c).
onStart(availableOffset)
- return onScroll(availableOffset)
+ return if (skipScroll) 0f else scroll(availableOffset, source)
}
- private fun onPriorityStop(velocity: Float): SuspendedValue<Float> {
- // We can restart tracking the consumed offsets from scratch.
- offsetScrolledBeforePriorityMode = 0f
+ private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
+ // Step 2: We have the priority and can consume the scroll events.
+ val consumedByScroll = onScroll(offsetAvailable, source)
- if (!isPriorityMode) {
- return { 0f }
+ if (shouldStop(consumedByScroll)) {
+ // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+ cancel()
+
+ // We've just reset offsetScrolledBeforePriorityMode to 0f
+ // We want to track the amount of offset consumed before entering priority mode
+ offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
}
+ return consumedByScroll
+ }
+
+ /** Reset the tracking of consumed offsets before entering in priority mode. */
+ private fun resetOffsetTracker() {
+ offsetScrolledBeforePriorityMode = 0f
+ }
+
+ private suspend fun stop(velocityAvailable: Float): Float {
+ check(isPriorityMode) { "This should never happen, stop() was called before start()" }
isPriorityMode = false
+ resetOffsetTracker()
- return onStop(velocity)
+ return coroutineScope {
+ onStopJob = async { onStop(velocityAvailable) }
+ onStopJob.await()
+ }
+ }
+
+ private fun cancel() {
+ check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
+ isPriorityMode = false
+ resetOffsetTracker()
+ onCancel()
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index ecef6be49df8..57b9423e85d1 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -39,7 +39,6 @@ import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
-import com.android.compose.nestedscroll.SuspendedValue
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
import com.android.compose.test.transition
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index c8f6e6d99933..3df608717414 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -46,7 +46,6 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.modifiers.thenIf
-import com.android.compose.nestedscroll.SuspendedValue
import com.google.common.truth.Truth.assertThat
import kotlin.properties.Delegates
import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index f3a34884c756..f5bb5ba032c2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -727,4 +727,45 @@ class SceneTransitionLayoutStateTest {
// The previous job is cancelled and does not infinitely collect the progress.
job.join()
}
+
+ @Test
+ fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+
+ val aToB =
+ transition(
+ SceneA,
+ SceneB,
+ onFreezeAndAnimate = {
+ // Do nothing, so that this transition stays in the transitionStates list and we
+ // can finish() it manually later.
+ },
+ )
+ val replacingAToB = transition(SceneB, SceneC)
+ val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB)
+
+ // Start A => B.
+ val aToBJob = state.startTransitionImmediately(animationScope = this, aToB)
+
+ // Start B => C and immediately finish it. It will be flagged as finished in
+ // STLState.finishedTransitions given that A => B is not finished yet.
+ val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB)
+ replacingAToB.finish()
+ bToCJob.join()
+
+ // Start a new B => C that replaces the previously finished B => C.
+ val replacingBToCJob =
+ state.startTransitionImmediately(animationScope = this, replacingBToC)
+
+ // Finish A => B.
+ aToB.finish()
+ aToBJob.join()
+
+ // Finish the new B => C.
+ replacingBToC.finish()
+ replacingBToCJob.join()
+
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneC)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 28d0a473935d..1711f3145cae 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -102,26 +102,22 @@ class SwipeToSceneTest {
modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName),
) {
scene(
- SceneA,
+ key = SceneA,
userActions =
if (swipesEnabled())
- mapOf(
- Swipe.Left to SceneB,
- Swipe.Down to TestScenes.SceneC,
- Swipe.Up to SceneB,
- )
+ mapOf(Swipe.Left to SceneB, Swipe.Down to SceneC, Swipe.Up to SceneB)
else emptyMap(),
) {
Box(Modifier.fillMaxSize())
}
scene(
- SceneB,
+ key = SceneB,
userActions = if (swipesEnabled()) mapOf(Swipe.Right to SceneA) else emptyMap(),
) {
Box(Modifier.fillMaxSize())
}
scene(
- TestScenes.SceneC,
+ key = SceneC,
userActions =
if (swipesEnabled())
mapOf(
@@ -196,7 +192,7 @@ class SwipeToSceneTest {
// Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
assertThat(transition).hasCurrentScene(SceneA)
assertThat(transition).hasProgress(56.dp / LayoutHeight)
assertThat(transition).isInitiatedByUserInput()
@@ -206,15 +202,15 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
- assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
+ assertThat(transition).hasCurrentScene(SceneC)
assertThat(transition).hasProgress(56.dp / LayoutHeight)
assertThat(transition).isInitiatedByUserInput()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
@@ -271,20 +267,20 @@ class SwipeToSceneTest {
// We should be animating to C (currentScene = SceneC).
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
- assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
+ assertThat(transition).hasCurrentScene(SceneC)
assertThat(transition).hasProgress(55.dp / LayoutHeight)
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
fun multiPointerSwipe() {
// Start at scene C.
- val layoutState = layoutState(TestScenes.SceneC)
+ val layoutState = layoutState(SceneC)
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
@@ -295,7 +291,7 @@ class SwipeToSceneTest {
}
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe down with two fingers.
rule.onRoot().performTouchInput {
@@ -307,7 +303,7 @@ class SwipeToSceneTest {
// We are transitioning to B because we used 2 fingers.
val transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -315,13 +311,13 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
fun defaultEdgeSwipe() {
// Start at scene C.
- val layoutState = layoutState(TestScenes.SceneC)
+ val layoutState = layoutState(SceneC)
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
@@ -332,7 +328,7 @@ class SwipeToSceneTest {
}
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe down from the top edge.
rule.onRoot().performTouchInput {
@@ -342,7 +338,7 @@ class SwipeToSceneTest {
// We are transitioning to B (and not A) because we started from the top edge.
var transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -350,7 +346,7 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe right from the left edge.
rule.onRoot().performTouchInput {
@@ -360,7 +356,7 @@ class SwipeToSceneTest {
// We are transitioning to B (and not A) because we started from the left edge.
transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -368,7 +364,7 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
@@ -434,7 +430,7 @@ class SwipeToSceneTest {
// We should still correctly compute that we are swiping down to scene C.
var transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasToScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
// Release the finger, animating back to scene A.
rule.onRoot().performTouchInput { up() }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index badc43bd3e0f..1a3b86b936df 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -34,30 +34,31 @@ class PriorityNestedScrollConnectionTest {
private var canStartPreScroll = false
private var canStartPostScroll = false
private var canStartPostFling = false
- private var canContinueScroll = false
+ private var canStopOnPreFling = true
private var isStarted = false
private var lastScroll: Float? = null
- private var returnOnScroll = 0f
+ private var consumeScroll = true
private var lastStop: Float? = null
- private var returnOnStop = 0f
+ private var isCancelled: Boolean = false
+ private var consumeStop = true
private val scrollConnection =
PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
- canStartPreScroll = { _, _ -> canStartPreScroll },
- canStartPostScroll = { _, _ -> canStartPostScroll },
+ canStartPreScroll = { _, _, _ -> canStartPreScroll },
+ canStartPostScroll = { _, _, _ -> canStartPostScroll },
canStartPostFling = { canStartPostFling },
- canContinueScroll = { canContinueScroll },
- canScrollOnFling = false,
+ canStopOnPreFling = { canStopOnPreFling },
onStart = { isStarted = true },
- onScroll = {
- lastScroll = it
- returnOnScroll
+ onScroll = { offsetAvailable, _ ->
+ lastScroll = offsetAvailable
+ if (consumeScroll) offsetAvailable else 0f
},
onStop = {
lastStop = it
- { returnOnStop }
+ if (consumeStop) it else 0f
},
+ onCancel = { isCancelled = true },
)
@Test
@@ -85,7 +86,7 @@ class PriorityNestedScrollConnectionTest {
canStartPostScroll = true
scrollConnection.onPostScroll(
consumed = Offset.Zero,
- available = Offset.Zero,
+ available = Offset(1f, 1f),
source = UserInput,
)
}
@@ -136,45 +137,55 @@ class PriorityNestedScrollConnectionTest {
@Test
fun step2_onPriorityMode_shouldContinueIfAllowed() {
startPriorityModePostScroll()
- canContinueScroll = true
- scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput)
+ val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
assertThat(lastScroll).isEqualTo(1f)
+ assertThat(scroll1.y).isEqualTo(1f)
- canContinueScroll = false
- scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput)
- assertThat(lastScroll).isNotEqualTo(2f)
- assertThat(lastScroll).isEqualTo(1f)
+ consumeScroll = false
+ val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput)
+ assertThat(lastScroll).isEqualTo(2f)
+ assertThat(scroll2.y).isEqualTo(0f)
}
@Test
- fun step3a_onPriorityMode_shouldStopIfCannotContinue() {
+ fun step3a_onPriorityMode_shouldCancelIfCannotContinue() {
startPriorityModePostScroll()
- canContinueScroll = false
+ consumeScroll = false
- scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
+ scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
- assertThat(lastStop).isNotNull()
+ assertThat(isCancelled).isTrue()
}
@Test
fun step3b_onPriorityMode_shouldStopOnFling() = runTest {
startPriorityModePostScroll()
- canContinueScroll = true
scrollConnection.onPreFling(available = Velocity.Zero)
- assertThat(lastStop).isNotNull()
+ assertThat(lastStop).isEqualTo(0f)
+ }
+
+ @Test
+ fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest {
+ startPriorityModePostScroll()
+ canStopOnPreFling = false
+
+ scrollConnection.onPreFling(available = Velocity.Zero)
+ assertThat(lastStop).isNull()
+
+ scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
+ assertThat(lastStop).isEqualTo(0f)
}
@Test
- fun step3c_onPriorityMode_shouldStopOnReset() {
+ fun step3c_onPriorityMode_shouldCancelOnReset() {
startPriorityModePostScroll()
- canContinueScroll = true
scrollConnection.reset()
- assertThat(lastStop).isNotNull()
+ assertThat(isCancelled).isTrue()
}
@Test
diff --git a/packages/SystemUI/customization/res/values/ids.xml b/packages/SystemUI/customization/res/values/ids.xml
index ec466f041179..3a3e06bdd377 100644
--- a/packages/SystemUI/customization/res/values/ids.xml
+++ b/packages/SystemUI/customization/res/values/ids.xml
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <item type="id" name="lockscreen_clock_view_large" />
+ <item type="id" name="lockscreen_clock_view" />
+
<!-- View ids for elements in large weather clock -->
<item type="id" name="weather_clock_time" />
<item type="id" name="weather_clock_date" />
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 9b94c91a348c..eedddb28ff89 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -30,16 +30,16 @@ import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockReactiveSetting
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
-import com.android.systemui.shared.clocks.view.DigitalClockFaceView
import com.android.systemui.shared.clocks.view.FlexClockView
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
import java.util.Locale
import java.util.TimeZone
class ComposedDigitalLayerController(
private val ctx: Context,
- private val assets: AssetLoader,
+ private val resources: Resources,
+ private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
private val layer: ComposedDigitalHandLayer,
- private val isLargeClock: Boolean,
messageBuffer: MessageBuffer,
) : SimpleClockLayerController {
private val logger = Logger(messageBuffer, ComposedDigitalLayerController::class.simpleName!!)
@@ -48,34 +48,22 @@ class ComposedDigitalLayerController(
val dozeState = DefaultClockController.AnimationState(1F)
var isRegionDark = true
- override var view: DigitalClockFaceView =
- when (layer.customizedView) {
- "FlexClockView" -> FlexClockView(ctx, assets, messageBuffer)
- else -> {
- throw IllegalStateException("CustomizedView string is not valid")
- }
- }
-
- // Matches LayerControllerConstructor
- internal constructor(
- ctx: Context,
- assets: AssetLoader,
- layer: ClockLayer,
- isLargeClock: Boolean,
- messageBuffer: MessageBuffer,
- ) : this(ctx, assets, layer as ComposedDigitalHandLayer, isLargeClock, messageBuffer)
+ override val view = FlexClockView(ctx, assets, messageBuffer)
init {
layer.digitalLayers.forEach {
+ val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
val controller =
- SimpleClockLayerController.Factory.create(
+ SimpleDigitalHandLayerController(
ctx,
+ resources,
assets,
- it,
- isLargeClock,
+ it as DigitalHandLayer,
+ childView,
messageBuffer,
)
- view.addView(controller.view)
+
+ view.addView(childView)
layerControllers.add(controller)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index ac268420fb75..3903dbaf64c6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -53,11 +53,8 @@ class DefaultClockProvider(
}
return if (clockReactiveVariants) {
- // TODO handle the case here where only the smallClock message buffer is added
- val assetLoader =
- AssetLoader(ctx, ctx, "clocks/", messageBuffers?.smallClockMessageBuffer!!)
-
- SimpleClockController(ctx, assetLoader, FLEX_DESIGN, messageBuffers)
+ val assets = AssetLoader(ctx, ctx, "clocks/", messageBuffers!!.infraMessageBuffer)
+ FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers)
} else {
DefaultClockController(
ctx,
@@ -82,6 +79,9 @@ class DefaultClockProvider(
resources.getString(R.string.clock_default_description),
// TODO(b/352049256): Update placeholder to actual resource
resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+ isReactiveToTone = true,
+ isReactiveToTouch = clockReactiveVariants,
+ axes = listOf(), // TODO: Ater some picker definition
)
}
@@ -118,9 +118,9 @@ class DefaultClockProvider(
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER
+ VerticalAlignment.CENTER,
),
- dateTimeFormat = "hh"
+ dateTimeFormat = "hh",
),
DigitalHandLayer(
layerBounds = LayerBounds.FIT,
@@ -146,9 +146,9 @@ class DefaultClockProvider(
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER
+ VerticalAlignment.CENTER,
),
- dateTimeFormat = "hh"
+ dateTimeFormat = "hh",
),
DigitalHandLayer(
layerBounds = LayerBounds.FIT,
@@ -174,9 +174,9 @@ class DefaultClockProvider(
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER
+ VerticalAlignment.CENTER,
),
- dateTimeFormat = "mm"
+ dateTimeFormat = "mm",
),
DigitalHandLayer(
layerBounds = LayerBounds.FIT,
@@ -202,11 +202,11 @@ class DefaultClockProvider(
alignment =
DigitalAlignment(
HorizontalAlignment.CENTER,
- VerticalAlignment.CENTER
+ VerticalAlignment.CENTER,
),
- dateTimeFormat = "mm"
- )
- )
+ dateTimeFormat = "mm",
+ ),
+ ),
)
)
@@ -230,7 +230,7 @@ class DefaultClockProvider(
renderType = RenderType.CHANGE_WEIGHT,
),
alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
- dateTimeFormat = "h:mm"
+ dateTimeFormat = "h:mm",
)
)
@@ -239,7 +239,7 @@ class DefaultClockProvider(
name = "@string/clock_default_name",
description = "@string/clock_default_description",
large = ClockFace(layers = largeLayer),
- small = ClockFace(layers = smallLayer)
+ small = ClockFace(layers = smallLayer),
)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index ec7779825bda..b8ebd0ff559b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -18,7 +18,7 @@ package com.android.systemui.shared.clocks
import android.content.Context
import android.content.res.Resources
-import com.android.systemui.monet.Style as MonetStyle
+import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
@@ -27,21 +27,24 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockReactiveSetting
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.FlexClockView
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
-/** Controller for a simple json specified clock */
-class SimpleClockController(
+/** Controller for the default flex clock */
+class FlexClockController(
private val ctx: Context,
- private val assets: AssetLoader,
- val design: ClockDesign,
+ private val resources: Resources,
+ private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ val design: ClockDesign, // TODO(b/364680879): Remove when done inlining
val messageBuffers: ClockMessageBuffers?,
) : ClockController {
override val smallClock = run {
val buffer = messageBuffers?.smallClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
- SimpleClockFaceController(
+ FlexClockFaceController(
ctx,
+ resources,
assets.copy(messageBuffer = buffer),
design.small ?: design.large!!,
false,
@@ -51,8 +54,9 @@ class SimpleClockController(
override val largeClock = run {
val buffer = messageBuffers?.largeClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
- SimpleClockFaceController(
+ FlexClockFaceController(
ctx,
+ resources,
assets.copy(messageBuffer = buffer),
design.large ?: design.small!!,
true,
@@ -62,16 +66,10 @@ class SimpleClockController(
override val config: ClockConfig by lazy {
ClockConfig(
- design.id,
- design.name?.let { assets.tryReadString(it) ?: it } ?: "",
- design.description?.let { assets.tryReadString(it) ?: it } ?: "",
- isReactiveToTone =
- design.colorPalette == null || design.colorPalette == MonetStyle.CLOCK,
- useAlternateSmartspaceAODTransition =
- smallClock.config.hasCustomWeatherDataDisplay ||
- largeClock.config.hasCustomWeatherDataDisplay,
- useCustomClockScene =
- smallClock.config.useCustomClockScene || largeClock.config.useCustomClockScene,
+ DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description),
+ isReactiveToTone = true,
)
}
@@ -80,8 +78,8 @@ class SimpleClockController(
override var isReactiveTouchInteractionEnabled = false
set(value) {
field = value
- smallClock.events.isReactiveTouchInteractionEnabled = value
- largeClock.events.isReactiveTouchInteractionEnabled = value
+ val view = largeClock.view as FlexClockView
+ view.isReactiveTouchInteractionEnabled = value
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
new file mode 100644
index 000000000000..ef24d2ad3071
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -0,0 +1,261 @@
+/*
+ * 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.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.widget.FrameLayout
+import com.android.systemui.customization.R
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFaceConfig
+import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.FlexClockView
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import java.util.Locale
+import java.util.TimeZone
+import kotlin.math.max
+
+// TODO(b/364680879): Merge w/ ComposedDigitalLayerController
+class FlexClockFaceController(
+ ctx: Context,
+ private val resources: Resources,
+ val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+ face: ClockFace,
+ private val isLargeClock: Boolean,
+ messageBuffer: MessageBuffer,
+) : ClockFaceController {
+ override val view: View
+ get() = layerController.view
+
+ override val config =
+ ClockFaceConfig(
+ hasCustomPositionUpdatedAnimation = false // TODO(b/364673982)
+ )
+
+ private val keyguardLargeClockTopMargin =
+ resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
+ val layerController: SimpleClockLayerController
+ val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")
+
+ init {
+ val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+ lp.gravity = Gravity.CENTER
+
+ val layer = face.layers[0]
+
+ layerController =
+ if (isLargeClock)
+ ComposedDigitalLayerController(
+ ctx,
+ resources,
+ assets,
+ layer as ComposedDigitalHandLayer,
+ messageBuffer,
+ )
+ else {
+ val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
+ SimpleDigitalHandLayerController(
+ ctx,
+ resources,
+ assets,
+ layer as DigitalHandLayer,
+ childView,
+ messageBuffer,
+ )
+ }
+ layerController.view.layoutParams = lp
+ }
+
+ override val layout: ClockFaceLayout =
+ DefaultClockFaceLayout(view).apply {
+ views[0].id =
+ if (isLargeClock) R.id.lockscreen_clock_view_large else R.id.lockscreen_clock_view
+ }
+
+ override val events = FlexClockFaceEvents()
+
+ // TODO(b/364680879): Remove ClockEvents
+ inner class FlexClockFaceEvents : ClockEvents, ClockFaceEvents {
+ override var isReactiveTouchInteractionEnabled = false
+ get() = field
+ set(value) {
+ field = value
+ layerController.events.isReactiveTouchInteractionEnabled = value
+ }
+
+ override fun onTimeTick() {
+ timespecHandler.updateTime()
+ view.contentDescription = timespecHandler.getContentDescription()
+ layerController.faceEvents.onTimeTick()
+ }
+
+ override fun onTimeZoneChanged(timeZone: TimeZone) {
+ timespecHandler.timeZone = timeZone
+ layerController.events.onTimeZoneChanged(timeZone)
+ }
+
+ override fun onTimeFormatChanged(is24Hr: Boolean) {
+ timespecHandler.is24Hr = is24Hr
+ layerController.events.onTimeFormatChanged(is24Hr)
+ }
+
+ override fun onLocaleChanged(locale: Locale) {
+ timespecHandler.updateLocale(locale)
+ layerController.events.onLocaleChanged(locale)
+ }
+
+ override fun onFontSettingChanged(fontSizePx: Float) {
+ layerController.faceEvents.onFontSettingChanged(fontSizePx)
+ }
+
+ override fun onColorPaletteChanged(resources: Resources) {
+ layerController.events.onColorPaletteChanged(resources)
+ layerController.updateColors()
+ }
+
+ override fun onSeedColorChanged(seedColor: Int?) {
+ layerController.events.onSeedColorChanged(seedColor)
+ layerController.updateColors()
+ }
+
+ override fun onRegionDarknessChanged(isRegionDark: Boolean) {
+ layerController.faceEvents.onRegionDarknessChanged(isRegionDark)
+ }
+
+ override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+
+ /**
+ * targetRegion passed to all customized clock applies counter translationY of
+ * KeyguardStatusView and keyguard_large_clock_top_margin from default clock
+ */
+ override fun onTargetRegionChanged(targetRegion: Rect?) {
+ // When a clock needs to be aligned with screen, like weather clock
+ // it needs to offset back the translation of keyguard_large_clock_top_margin
+ if (isLargeClock && (view as FlexClockView).isAlignedWithScreen()) {
+ val topMargin = keyguardLargeClockTopMargin
+ targetRegion?.let {
+ val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock)
+ // In LS, we use yDiff to counter translate
+ // the translation of KeyguardLargeClockTopMargin
+ // With the targetRegion passed from picker,
+ // we will have yDiff = 0, no translation is needed for weather clock
+ if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2
+ }
+ return
+ }
+
+ var maxWidth = 0f
+ var maxHeight = 0f
+
+ layerController.faceEvents.onTargetRegionChanged(targetRegion)
+ maxWidth = max(maxWidth, view.layoutParams.width.toFloat())
+ maxHeight = max(maxHeight, view.layoutParams.height.toFloat())
+
+ val lp =
+ if (maxHeight <= 0 || maxWidth <= 0 || targetRegion == null) {
+ // No specified width/height. Just match parent size.
+ FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+ } else {
+ // Scale to fit in targetRegion based on largest child elements.
+ val ratio = maxWidth / maxHeight
+ val targetRatio = targetRegion.width() / targetRegion.height().toFloat()
+ val scale =
+ if (ratio > targetRatio) targetRegion.width() / maxWidth
+ else targetRegion.height() / maxHeight
+
+ FrameLayout.LayoutParams(
+ (maxWidth * scale).toInt(),
+ (maxHeight * scale).toInt(),
+ )
+ }
+
+ lp.gravity = Gravity.CENTER
+ view.layoutParams = lp
+ targetRegion?.let {
+ val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock)
+ view.translationX = xDiff
+ view.translationY = yDiff
+ }
+ }
+
+ override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
+
+ override fun onWeatherDataChanged(data: WeatherData) {
+ layerController.events.onWeatherDataChanged(data)
+ }
+
+ override fun onAlarmDataChanged(data: AlarmData) {
+ layerController.events.onAlarmDataChanged(data)
+ }
+
+ override fun onZenDataChanged(data: ZenData) {
+ layerController.events.onZenDataChanged(data)
+ }
+ }
+
+ override val animations =
+ object : ClockAnimations {
+ override fun enter() {
+ layerController.animations.enter()
+ }
+
+ override fun doze(fraction: Float) {
+ layerController.animations.doze(fraction)
+ }
+
+ override fun fold(fraction: Float) {
+ layerController.animations.fold(fraction)
+ }
+
+ override fun charge() {
+ layerController.animations.charge()
+ }
+
+ override fun onPickerCarouselSwiping(swipingFraction: Float) {
+ face.pickerScale?.let {
+ view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX
+ view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY
+ }
+ if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) {
+ view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction
+ }
+ layerController.animations.onPickerCarouselSwiping(swipingFraction)
+ view.invalidate()
+ }
+
+ override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
+ layerController.animations.onPositionUpdated(fromLeft, direction, fraction)
+ }
+
+ override fun onPositionUpdated(distance: Float, fraction: Float) {
+ layerController.animations.onPositionUpdated(distance, fraction)
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt
deleted file mode 100644
index ef398d1a52a0..000000000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.clocks
-
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Rect
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.widget.FrameLayout
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.plugins.clocks.AlarmData
-import com.android.systemui.plugins.clocks.ClockAnimations
-import com.android.systemui.plugins.clocks.ClockEvents
-import com.android.systemui.plugins.clocks.ClockFaceConfig
-import com.android.systemui.plugins.clocks.ClockFaceController
-import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFaceLayout
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
-import com.android.systemui.plugins.clocks.ClockTickRate
-import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
-import com.android.systemui.plugins.clocks.WeatherData
-import com.android.systemui.plugins.clocks.ZenData
-import com.android.systemui.shared.clocks.view.DigitalClockFaceView
-import java.util.Locale
-import java.util.TimeZone
-import kotlin.math.max
-
-interface ClockEventUnion : ClockEvents, ClockFaceEvents
-
-class SimpleClockFaceController(
- ctx: Context,
- val assets: AssetLoader,
- face: ClockFace,
- isLargeClock: Boolean,
- messageBuffer: MessageBuffer,
-) : ClockFaceController {
- override val view: View
- override val config: ClockFaceConfig by lazy {
- ClockFaceConfig(
- hasCustomWeatherDataDisplay = layers.any { it.config.hasCustomWeatherDataDisplay },
- hasCustomPositionUpdatedAnimation =
- layers.any { it.config.hasCustomPositionUpdatedAnimation },
- tickRate = getTickRate(),
- useCustomClockScene = layers.any { it.config.useCustomClockScene },
- )
- }
-
- val layers = mutableListOf<SimpleClockLayerController>()
-
- val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")
-
- init {
- val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
- lp.gravity = Gravity.CENTER
- view =
- if (face.layers.size == 1) {
- // Optimize a clocks with a single layer by excluding the face level view group. We
- // expect the view container from the host process to always be a FrameLayout.
- val layer = face.layers[0]
- val controller =
- SimpleClockLayerController.Factory.create(
- ctx,
- assets,
- layer,
- isLargeClock,
- messageBuffer,
- )
- layers.add(controller)
- controller.view.layoutParams = lp
- controller.view
- } else {
- // For multiple views, we use an intermediate RelativeLayout so that we can do some
- // intelligent laying out between the children views.
- val group = SimpleClockRelativeLayout(ctx, face.faceLayout)
- group.layoutParams = lp
- group.gravity = Gravity.CENTER
- group.clipChildren = false
- for (layer in face.layers) {
- face.faceLayout?.let {
- if (layer is DigitalHandLayer) {
- layer.faceLayout = it
- }
- }
- val controller =
- SimpleClockLayerController.Factory.create(
- ctx,
- assets,
- layer,
- isLargeClock,
- messageBuffer,
- )
- group.addView(controller.view)
- layers.add(controller)
- }
- group
- }
- }
-
- override val layout: ClockFaceLayout =
- DefaultClockFaceLayout(view).apply {
- views[0].id =
- if (isLargeClock) {
- assets.getResourcesId("lockscreen_clock_view_large")
- } else {
- assets.getResourcesId("lockscreen_clock_view")
- }
- }
-
- override val events =
- object : ClockEventUnion {
- override var isReactiveTouchInteractionEnabled = false
- get() = field
- set(value) {
- field = value
- layers.forEach { it.events.isReactiveTouchInteractionEnabled = value }
- }
-
- override fun onTimeTick() {
- timespecHandler.updateTime()
- if (
- config.tickRate == ClockTickRate.PER_MINUTE ||
- view.contentDescription != timespecHandler.getContentDescription()
- ) {
- view.contentDescription = timespecHandler.getContentDescription()
- }
- layers.forEach { it.faceEvents.onTimeTick() }
- }
-
- override fun onTimeZoneChanged(timeZone: TimeZone) {
- timespecHandler.timeZone = timeZone
- layers.forEach { it.events.onTimeZoneChanged(timeZone) }
- }
-
- override fun onTimeFormatChanged(is24Hr: Boolean) {
- timespecHandler.is24Hr = is24Hr
- layers.forEach { it.events.onTimeFormatChanged(is24Hr) }
- }
-
- override fun onLocaleChanged(locale: Locale) {
- timespecHandler.updateLocale(locale)
- layers.forEach { it.events.onLocaleChanged(locale) }
- }
-
- override fun onFontSettingChanged(fontSizePx: Float) {
- layers.forEach { it.faceEvents.onFontSettingChanged(fontSizePx) }
- }
-
- override fun onColorPaletteChanged(resources: Resources) {
- layers.forEach {
- it.events.onColorPaletteChanged(resources)
- it.updateColors()
- }
- }
-
- override fun onSeedColorChanged(seedColor: Int?) {
- layers.forEach {
- it.events.onSeedColorChanged(seedColor)
- it.updateColors()
- }
- }
-
- override fun onRegionDarknessChanged(isRegionDark: Boolean) {
- layers.forEach { it.faceEvents.onRegionDarknessChanged(isRegionDark) }
- }
-
- override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
-
- /**
- * targetRegion passed to all customized clock applies counter translationY of
- * KeyguardStatusView and keyguard_large_clock_top_margin from default clock
- */
- override fun onTargetRegionChanged(targetRegion: Rect?) {
- // When a clock needs to be aligned with screen, like weather clock
- // it needs to offset back the translation of keyguard_large_clock_top_margin
- if (view is DigitalClockFaceView && view.isAlignedWithScreen()) {
- val topMargin = getKeyguardLargeClockTopMargin(assets)
- targetRegion?.let {
- val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock)
- // In LS, we use yDiff to counter translate
- // the translation of KeyguardLargeClockTopMargin
- // With the targetRegion passed from picker,
- // we will have yDiff = 0, no translation is needed for weather clock
- if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2
- }
- return
- }
-
- var maxWidth = 0f
- var maxHeight = 0f
-
- for (layer in layers) {
- layer.faceEvents.onTargetRegionChanged(targetRegion)
- maxWidth = max(maxWidth, layer.view.layoutParams.width.toFloat())
- maxHeight = max(maxHeight, layer.view.layoutParams.height.toFloat())
- }
-
- val lp =
- if (maxHeight <= 0 || maxWidth <= 0 || targetRegion == null) {
- // No specified width/height. Just match parent size.
- FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
- } else {
- // Scale to fit in targetRegion based on largest child elements.
- val ratio = maxWidth / maxHeight
- val targetRatio = targetRegion.width() / targetRegion.height().toFloat()
- val scale =
- if (ratio > targetRatio) targetRegion.width() / maxWidth
- else targetRegion.height() / maxHeight
-
- FrameLayout.LayoutParams(
- (maxWidth * scale).toInt(),
- (maxHeight * scale).toInt(),
- )
- }
-
- lp.gravity = Gravity.CENTER
- view.layoutParams = lp
- targetRegion?.let {
- val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock)
- view.translationX = xDiff
- view.translationY = yDiff
- }
- }
-
- override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
-
- override fun onWeatherDataChanged(data: WeatherData) {
- layers.forEach { it.events.onWeatherDataChanged(data) }
- }
-
- override fun onAlarmDataChanged(data: AlarmData) {
- layers.forEach { it.events.onAlarmDataChanged(data) }
- }
-
- override fun onZenDataChanged(data: ZenData) {
- layers.forEach { it.events.onZenDataChanged(data) }
- }
- }
-
- override val animations =
- object : ClockAnimations {
- override fun enter() {
- layers.forEach { it.animations.enter() }
- }
-
- override fun doze(fraction: Float) {
- layers.forEach { it.animations.doze(fraction) }
- }
-
- override fun fold(fraction: Float) {
- layers.forEach { it.animations.fold(fraction) }
- }
-
- override fun charge() {
- layers.forEach { it.animations.charge() }
- }
-
- override fun onPickerCarouselSwiping(swipingFraction: Float) {
- face.pickerScale?.let {
- view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX
- view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY
- }
- if (!(view is DigitalClockFaceView && view.isAlignedWithScreen())) {
- val topMargin = getKeyguardLargeClockTopMargin(assets)
- view.translationY = topMargin / 2F * swipingFraction
- }
- layers.forEach { it.animations.onPickerCarouselSwiping(swipingFraction) }
- view.invalidate()
- }
-
- override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
- layers.forEach { it.animations.onPositionUpdated(fromLeft, direction, fraction) }
- }
-
- override fun onPositionUpdated(distance: Float, fraction: Float) {
- layers.forEach { it.animations.onPositionUpdated(distance, fraction) }
- }
- }
-
- private fun getTickRate(): ClockTickRate {
- var tickRate = ClockTickRate.PER_MINUTE
- for (layer in layers) {
- if (layer.config.tickRate.value < tickRate.value) {
- tickRate = layer.config.tickRate
- }
- }
- return tickRate
- }
-
- private fun getKeyguardLargeClockTopMargin(assets: AssetLoader): Int {
- val topMarginRes =
- assets.resolveResourceId(null, "dimen", "keyguard_large_clock_top_margin")
- if (topMarginRes != null) {
- val (res, id) = topMarginRes
- return res.getDimensionPixelSize(id)
- }
- return 0
- }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
index f71543efa650..5d1a2dbc4209 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
@@ -16,25 +16,12 @@
package com.android.systemui.shared.clocks
-import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
-import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
-import kotlin.reflect.KClass
-
-typealias LayerControllerConstructor =
- (
- ctx: Context,
- assets: AssetLoader,
- layer: ClockLayer,
- isLargeClock: Boolean,
- messageBuffer: MessageBuffer,
- ) -> SimpleClockLayerController
interface SimpleClockLayerController {
val view: View
@@ -48,55 +35,4 @@ interface SimpleClockLayerController {
// Called immediately after either onColorPaletteChanged or onSeedColorChanged is called.
// Provided for convience to not duplicate color update logic after state updated.
fun updateColors() {}
-
- companion object Factory {
- val constructorMap = mutableMapOf<Pair<KClass<*>, KClass<*>?>, LayerControllerConstructor>()
-
- internal inline fun <reified TLayer> registerConstructor(
- noinline constructor: LayerControllerConstructor,
- ) where TLayer : ClockLayer {
- constructorMap[Pair(TLayer::class, null)] = constructor
- }
-
- inline fun <reified TLayer, reified TStyle> registerTextConstructor(
- noinline constructor: LayerControllerConstructor,
- ) where TLayer : ClockLayer, TStyle : TextStyle {
- constructorMap[Pair(TLayer::class, TStyle::class)] = constructor
- }
-
- init {
- registerConstructor<ComposedDigitalHandLayer>(::ComposedDigitalLayerController)
- registerTextConstructor<DigitalHandLayer, FontTextStyle>(::createSimpleDigitalLayer)
- }
-
- private fun createSimpleDigitalLayer(
- ctx: Context,
- assets: AssetLoader,
- layer: ClockLayer,
- isLargeClock: Boolean,
- messageBuffer: MessageBuffer
- ): SimpleClockLayerController {
- val view = SimpleDigitalClockTextView(ctx, messageBuffer)
- return SimpleDigitalHandLayerController(
- ctx,
- assets,
- layer as DigitalHandLayer,
- view,
- messageBuffer
- )
- }
-
- fun create(
- ctx: Context,
- assets: AssetLoader,
- layer: ClockLayer,
- isLargeClock: Boolean,
- messageBuffer: MessageBuffer
- ): SimpleClockLayerController {
- val styleClass = if (layer is DigitalHandLayer) layer.style::class else null
- val key = Pair(layer::class, styleClass)
- return constructorMap[key]?.invoke(ctx, assets, layer, isLargeClock, messageBuffer)
- ?: throw IllegalArgumentException("Unrecognized ClockLayer type: $key")
- }
- }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index a3240f81e499..ce1eae48546a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -42,7 +42,8 @@ private val TAG = SimpleDigitalHandLayerController::class.simpleName!!
open class SimpleDigitalHandLayerController<T>(
private val ctx: Context,
- private val assets: AssetLoader,
+ private val resources: Resources,
+ private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
private val layer: DigitalHandLayer,
override val view: T,
messageBuffer: MessageBuffer,
@@ -68,7 +69,7 @@ open class SimpleDigitalHandLayerController<T>(
view.layoutParams =
RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
if (layer.alignment != null) {
layer.alignment.verticalAlignment?.let { view.verticalAlignment = it }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
index eb7234646a64..81efcb9de4d8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
@@ -31,6 +31,7 @@ import com.android.systemui.shared.clocks.AssetLoader
import com.android.systemui.shared.clocks.LogUtil
import java.util.Locale
+// TODO(b/364680879): Merge w/ only subclass FlexClockView
abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) : FrameLayout(ctx) {
protected val logger = Logger(messageBuffer, this::class.simpleName!!)
get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
@@ -140,7 +141,6 @@ abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer)
open val useCustomClockScene
get() = false
- // TODO: implement ClockEventUnion?
open fun onLocaleChanged(locale: Locale) {}
open fun onWeatherDataChanged(data: WeatherData) {}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index c29c8dac8ba6..25b2ad772b32 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -35,7 +35,7 @@ import kotlin.math.min
fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal)
-class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffer: MessageBuffer) :
+class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: MessageBuffer) :
DigitalClockFaceView(context, messageBuffer) {
override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
val digitLeftTopMap = mutableMapOf<Int, Point>()
@@ -57,11 +57,9 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe
private var prevY = 0f
private var isDown = false
- // TODO(b/340253296): Genericize; json spec
private var wght = 603f
private var wdth = 100f
- // TODO(b/340253296): Json spec
private val MAX_WGHT = 950f
private val MIN_WGHT = 50f
private val WGHT_SCALE = 0.5f
@@ -71,7 +69,6 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe
private val WDTH_SCALE = 0.2f
override fun onTouchEvent(evt: MotionEvent): Boolean {
- // TODO(b/340253296): implement on DigitalClockFaceView?
if (!isReactiveTouchInteractionEnabled) {
return super.onTouchEvent(evt)
}
@@ -94,12 +91,11 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe
prevX = evt.x
prevY = evt.y
- // TODO(b/340253296): Genericize; json spec
val fvar = "'wght' $wght, 'wdth' $wdth, 'opsz' 144, 'ROND' 100"
digitalClockTextViewMap.forEach { (_, view) ->
val textStyle = view.textStyle as FontTextStyle
textStyle.fontVariation = fvar
- view.applyStyles(assetLoader, textStyle, view.aodStyle)
+ view.applyStyles(assets, textStyle, view.aodStyle)
}
requestLayout()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 2bb9e68a357a..00c5577b8017 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -156,8 +156,12 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase {
when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
.thenReturn(INVISIBLE);
- when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
- when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame);
+ when(mView
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large))
+ .thenReturn(mLargeClockFrame);
+ when(mView
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view))
+ .thenReturn(mSmallClockFrame);
when(mSmallClockView.getContext()).thenReturn(getContext());
when(mLargeClockView.getContext()).thenReturn(getContext());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 0bf9d12a09d5..4ed5fd0a6e71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -113,8 +113,10 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
});
mKeyguardClockSwitch =
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
- mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
- mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
+ mSmallClockFrame = mKeyguardClockSwitch
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
+ mLargeClockFrame = mKeyguardClockSwitch
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area);
mKeyguardClockSwitch.mChildrenAreLaidOut = true;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 2e41246a62a1..245388c214a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -17,10 +17,10 @@
package com.android.keyguard
import android.view.View
-import android.view.ViewGroup
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -88,7 +88,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
underTest.statusViewCentered = true
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
view
)
@@ -110,7 +110,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
whenever(statusBarStateController.getState()).thenReturn(SHADE)
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
view
)
@@ -134,7 +134,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
val view = View(context)
whenever(
notificationShadeWindowView
- .findViewById<View>(R.id.lockscreen_clock_view_large)
+ .findViewById<View>(customR.id.lockscreen_clock_view_large)
).thenReturn(view)
progressListener.onTransitionStarted()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 6c6de61c638a..cd8b2e12a3d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -440,6 +440,28 @@ class DisplayRepositoryTest : SysuiTestCase() {
}
@Test
+ fun displayAdditionEvent_emptyByDefault() =
+ testScope.runTest {
+ setDisplays(1, 2, 3)
+
+ val lastAddedDisplay by lastDisplayAdditionEvent()
+
+ assertThat(lastAddedDisplay).isNull()
+ }
+
+ @Test
+ fun displayAdditionEvent_displaysAdded_doesNotReplayEventsToNewSubscribers() =
+ testScope.runTest {
+ val priorDisplayAdded by lastDisplayAdditionEvent()
+ setDisplays(1)
+ sendOnDisplayAdded(1)
+ assertThat(priorDisplayAdded?.displayId).isEqualTo(1)
+
+ val lastAddedDisplay by collectLastValue(displayRepository.displayAdditionEvent)
+ assertThat(lastAddedDisplay).isNull()
+ }
+
+ @Test
fun defaultDisplayOff_changes() =
testScope.runTest {
val defaultDisplayOff by latestDefaultDisplayOffFlowValue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index 1c99eff0d328..d94c97af6f14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -28,12 +28,12 @@ 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.customization.R as customR
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
-import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.util.mockito.any
@@ -135,7 +135,7 @@ class SmartspaceSectionTest : SysuiTestCase() {
assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id)
val dateConstraints = constraintSet.getConstraint(dateView.id)
- assertThat(dateConstraints.layout.topToBottom).isEqualTo(R.id.lockscreen_clock_view)
+ assertThat(dateConstraints.layout.topToBottom).isEqualTo(customR.id.lockscreen_clock_view)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
index 14d60943149f..e5bdc2e56b11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
@@ -54,7 +54,7 @@ class PaginatedGridRepositoryTest : SysuiTestCase() {
private fun setRowsInConfig(rows: Int) =
with(kosmos) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_settings_max_rows,
+ R.integer.quick_settings_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
index ae6f576bcf3e..cda3d488cb1e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
@@ -54,7 +54,7 @@ class QuickQuickSettingsRowRepositoryTest : SysuiTestCase() {
private fun setRowsInConfig(rows: Int) =
with(kosmos) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_qs_panel_max_rows,
+ R.integer.quick_qs_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index a1c0ef2789d5..2c894f9aa20f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -151,7 +151,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
private fun Kosmos.setRows(rows: Int) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_qs_panel_max_rows,
+ R.integer.quick_qs_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0454317b5f04..06d19d7f9822 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -68,13 +68,13 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.EmptyLockIconViewController;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardSliceViewController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LegacyLockIconViewController;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -271,6 +271,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
+ @Mock protected EmptyLockIconViewController mLockIconViewController;
@Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@@ -285,7 +286,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected AmbientState mAmbientState;
@Mock protected UserManager mUserManager;
@Mock protected UiEventLogger mUiEventLogger;
- @Mock protected LegacyLockIconViewController mLockIconViewController;
@Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
@Mock protected KeyguardRootView mKeyguardRootView;
@Mock protected View mKeyguardRootViewChild;
@@ -397,7 +397,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
@@ -687,6 +686,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes);
when(longPressHandlingViewRes.getString(anyInt())).thenReturn("");
+ when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+ when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
mNotificationPanelViewController = new NotificationPanelViewController(
mView,
mMainHandler,
@@ -852,7 +854,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
mNotificationPanelViewController.cancelHeightAnimator();
leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream()
- .filter(Animator::isRunning).toList();
+ .filter(Animator::isRunning).toList();
mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel);
}
if (mMainHandler != null) {
@@ -869,11 +871,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
- when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
-
when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
- when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
- when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
.thenReturn(indicationPadding);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 43dbb40d7721..ec75972aecfe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -217,7 +217,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
}
@@ -235,7 +234,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -253,7 +251,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -271,7 +268,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
}
@@ -289,7 +285,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -389,7 +384,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
@DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void alternateBouncerVisible_onTouchEvent_notHandled() {
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
// GIVEN alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 905301eb38ae..943fb62003cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -74,6 +75,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var windowManager: WindowManager
@@ -105,6 +107,8 @@ class ShadeControllerImplTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(windowManager.defaultDisplay).thenReturn(display)
whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+ whenever(statusBarWindowControllerStore.defaultDisplay)
+ .thenReturn(statusBarWindowController)
shadeController =
ShadeControllerImpl(
commandQueue,
@@ -113,7 +117,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
keyguardStateController,
statusBarStateController,
statusBarKeyguardViewManager,
- statusBarWindowController,
+ statusBarWindowControllerStore,
deviceProvisionedController,
notificationShadeWindowController,
0,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index 9142972eabdd..f64387c95e3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.Assert.assertThrows
@@ -39,7 +40,8 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarInitializerTest : SysuiTestCase() {
- val windowController = mock(StatusBarWindowController::class.java)
+ private val windowController = mock(StatusBarWindowController::class.java)
+ private val windowControllerStore = mock(StatusBarWindowControllerStore::class.java)
@Before
fun setup() {
@@ -52,15 +54,16 @@ class StatusBarInitializerTest : SysuiTestCase() {
whenever(fragmentHostManager.fragmentManager).thenReturn(fragmentManager)
whenever(fragmentManager.beginTransaction()).thenReturn(transaction)
whenever(transaction.replace(any(), any(), any())).thenReturn(transaction)
-
+ whenever(windowControllerStore.defaultDisplay).thenReturn(windowController)
whenever(windowController.fragmentHostManager).thenReturn(fragmentHostManager)
}
val underTest =
StatusBarInitializerImpl(
- windowController,
- { mock(CollapsedStatusBarFragment::class.java) },
- setOf(),
+ displayId = context.displayId,
+ statusBarWindowControllerStore = windowControllerStore,
+ collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) },
+ creationListeners = setOf(),
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index 580336539c37..bb3fb1e71f78 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
import com.android.systemui.testKosmos
import com.android.wm.shell.bubbles.bubbles
import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
}
private val testScope = kosmos.testScope
private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
- private val statusBarWindowController = kosmos.fakeStatusBarWindowController
+ private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore
private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
private val notificationShadeWindowViewController =
@@ -94,7 +94,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
fun start_attachesWindow() {
orchestrator.start()
- assertThat(statusBarWindowController.isAttached).isTrue()
+ assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 984bda1c0d21..a629b2447921 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -53,6 +54,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var sbWindowController: StatusBarWindowController
+ @Mock private lateinit var sbWindowControllerStore: StatusBarWindowControllerStore
@Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
private var testView = TestView(mContext)
@@ -61,7 +63,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
-
+ whenever(sbWindowControllerStore.defaultDisplay).thenReturn(sbWindowController)
// StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to
// ensure that the chip view is added to a parent view
whenever(sbWindowController.addViewToWindow(any(), any())).then {
@@ -93,8 +95,8 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() {
controller =
SystemEventChipAnimationController(
context = mContext,
- statusBarWindowController = sbWindowController,
- contentInsetsProvider = insetsProvider
+ statusBarWindowControllerStore = sbWindowControllerStore,
+ contentInsetsProvider = insetsProvider,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
index 35e4047109d5..97fa6eb17b5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
@@ -31,6 +31,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
private var isStarted = false
+ private var wasStarted = false
private var scrimOffset = 0f
private var contentHeight = 0f
private var isCurrentGestureOverscroll = false
@@ -46,7 +47,10 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT },
isCurrentGestureOverscroll = { isCurrentGestureOverscroll },
onStart = { isStarted = true },
- onStop = { isStarted = false },
+ onStop = {
+ wasStarted = true
+ isStarted = false
+ },
)
@Test
@@ -180,6 +184,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
)
assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(wasStarted).isEqualTo(false)
assertThat(isStarted).isEqualTo(false)
}
@@ -196,7 +201,9 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
)
assertThat(offsetConsumed).isEqualTo(Offset.Zero)
- assertThat(isStarted).isEqualTo(true)
+ // Returning 0 offset will immediately stop the connection
+ assertThat(wasStarted).isEqualTo(true)
+ assertThat(isStarted).isEqualTo(false)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 179799503ac0..bac79a9cc520 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -94,6 +95,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Mock private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock private lateinit var statusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
@Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -112,6 +114,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController)
underTest =
LegacyActivityStarterInternalImpl(
centralSurfacesOptLazy = { Optional.of(centralSurfaces) },
@@ -128,7 +131,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
context = context,
displayId = DISPLAY_ID,
lockScreenUserManager = lockScreenUserManager,
- statusBarWindowController = statusBarWindowController,
+ statusBarWindowControllerStore = statusBarWindowControllerStore,
wakefulnessLifecycle = wakefulnessLifecycle,
keyguardStateController = keyguardStateController,
statusBarStateController = statusBarStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index 597e2e45ea14..e0d9fac0eba5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
@@ -74,6 +75,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
private const val CALL_UID = 900
@@ -106,6 +108,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
@Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var mockStatusBarWindowControllerStore: StatusBarWindowControllerStore
private lateinit var chipView: View
@@ -118,6 +121,8 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
val notificationCollection = mock(CommonNotifCollection::class.java)
+ whenever(mockStatusBarWindowControllerStore.defaultDisplay)
+ .thenReturn(mockStatusBarWindowController)
controller =
OngoingCallController(
@@ -131,7 +136,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
mainExecutor,
mockIActivityManager,
DumpManager(),
- mockStatusBarWindowController,
+ mockStatusBarWindowControllerStore,
mockSwipeStatusBarAwayGestureHandler,
statusBarModeRepository,
logcatLogBuffer("OngoingCallControllerViaListenerTest"),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
index dfe01bf45f38..2ad50cc38b7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -93,6 +94,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() {
private val mockActivityStarter = kosmos.activityStarter
private val mockIActivityManager = mock<IActivityManager>()
private val mockStatusBarWindowController = mock<StatusBarWindowController>()
+ private val mockStatusBarWindowControllerStore = mock<StatusBarWindowControllerStore>()
private lateinit var chipView: View
@@ -103,6 +105,8 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() {
chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
}
+ whenever(mockStatusBarWindowControllerStore.defaultDisplay)
+ .thenReturn(mockStatusBarWindowController)
controller =
OngoingCallController(
testScope.backgroundScope,
@@ -115,7 +119,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() {
mainExecutor,
mockIActivityManager,
DumpManager(),
- mockStatusBarWindowController,
+ mockStatusBarWindowControllerStore,
mockSwipeStatusBarAwayGestureHandler,
statusBarModeRepository,
logcatLogBuffer("OngoingCallControllerViaRepoTest"),
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index fc9c917c152b..8bef4759c55d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -26,7 +26,7 @@
android:clipChildren="false"
android:layout_gravity="center_horizontal|top">
<com.android.keyguard.KeyguardClockFrame
- android:id="@+id/lockscreen_clock_view"
+ android:id="@id/lockscreen_clock_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/small_clock_height"
android:layout_alignParentStart="true"
@@ -35,7 +35,7 @@
android:paddingStart="@dimen/clock_padding_start"
android:visibility="invisible" />
<com.android.keyguard.KeyguardClockFrame
- android:id="@+id/lockscreen_clock_view_large"
+ android:id="@id/lockscreen_clock_view_large"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index b5efeb5f6b3b..5d5b95546d0d 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -25,6 +25,12 @@
<integer name="quick_settings_num_columns">4</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">2</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">1</integer>
+
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">8</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index fc6d20e11d3b..c661846d025f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -27,6 +27,12 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">2</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 7daad1a43f73..f556b97eefc2 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -21,6 +21,12 @@
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">3</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6f94f9e2a216..16a8bc5b034f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -70,6 +70,12 @@
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">4</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">4</integer>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 4a96e9e0845a..c7ea98052b66 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -193,12 +193,16 @@ public class KeyguardClockSwitch extends RelativeLayout {
protected void onFinishInflate() {
super.onFinishInflate();
if (!MigrateClocksToBlueprint.isEnabled()) {
- mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
- mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
+ mSmallClockFrame = findViewById(
+ com.android.systemui.customization.R.id.lockscreen_clock_view);
+ mLargeClockFrame = findViewById(
+ com.android.systemui.customization.R.id.lockscreen_clock_view_large);
mStatusArea = findViewById(R.id.keyguard_status_area);
} else {
- removeView(findViewById(R.id.lockscreen_clock_view));
- removeView(findViewById(R.id.lockscreen_clock_view_large));
+ removeView(findViewById(
+ com.android.systemui.customization.R.id.lockscreen_clock_view));
+ removeView(findViewById(
+ com.android.systemui.customization.R.id.lockscreen_clock_view_large));
}
onConfigChanged();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index d468f2f0b0aa..7cba845460ca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -241,8 +241,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardSliceViewController.init();
if (!MigrateClocksToBlueprint.isEnabled()) {
- mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
- mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
+ mSmallClockFrame = mView
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
+ mLargeClockFrame = mView
+ .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
}
if (!mOnlyClock) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 63a4af949c8c..0684824ea0b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -534,7 +534,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
// KeyguardClockViewBinder
if (customClockAnimation && !MigrateClocksToBlueprint.isEnabled()) {
// Find the clock, so we can exclude it from this transition.
- FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large);
+ FrameLayout clockContainerView = mView.findViewById(
+ com.android.systemui.customization.R.id.lockscreen_clock_view_large);
// The clock container can sometimes be null. If it is, just fall back to the
// old animation rather than setting up the custom animations.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 19d918f5c556..5a02486d5096 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -18,6 +18,7 @@ package com.android.keyguard
import android.content.Context
import android.view.View
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -98,12 +99,12 @@ constructor(
viewsIdToTranslate =
setOf(
ViewIdToTranslate(
- viewId = R.id.lockscreen_clock_view_large,
+ viewId = customR.id.lockscreen_clock_view_large,
direction = START,
shouldBeAnimated = filterKeyguardAndSplitShadeOnly
),
ViewIdToTranslate(
- viewId = R.id.lockscreen_clock_view,
+ viewId = customR.id.lockscreen_clock_view,
direction = START,
shouldBeAnimated = filterKeyguard
),
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a30115568842..40c1f0f9895d 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -52,7 +52,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.tuner.TunerService;
import dagger.Lazy;
@@ -148,7 +148,7 @@ public class Dependency {
@Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy;
@Inject Lazy<DialogTransitionAnimator> mDialogTransitionAnimatorLazy;
@Inject Lazy<UserTracker> mUserTrackerLazy;
- @Inject Lazy<StatusBarWindowController> mStatusBarWindowControllerLazy;
+ @Inject Lazy<StatusBarWindowControllerStore> mStatusBarWindowControllerStoreLazy;
@Inject
public Dependency() {
@@ -192,7 +192,8 @@ public class Dependency {
mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get);
mProviders.put(DialogTransitionAnimator.class, mDialogTransitionAnimatorLazy::get);
mProviders.put(UserTracker.class, mUserTrackerLazy::get);
- mProviders.put(StatusBarWindowController.class, mStatusBarWindowControllerLazy::get);
+ mProviders.put(
+ StatusBarWindowControllerStore.class, mStatusBarWindowControllerStoreLazy::get);
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java
new file mode 100644
index 000000000000..1950d6bac251
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Default {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 6a6913677a0c..034cb31dbc74 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -33,6 +33,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.util.Compile
+import com.android.systemui.util.kotlin.pairwiseBy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -41,11 +42,12 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -146,11 +148,6 @@ constructor(
override val displayChangeEvent: Flow<Int> =
allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
- override val displayAdditionEvent: Flow<Display?> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
- getDisplayFromDisplayManager(it.displayId)
- }
-
override val displayRemovalEvent: Flow<Int> =
allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
@@ -212,6 +209,17 @@ constructor(
*/
override val displays: StateFlow<Set<Display>> = enabledDisplays
+ /**
+ * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
+ * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
+ * calling [getDisplay] for the newly added display will be non-null.
+ * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
+ */
+ override val displayAdditionEvent: Flow<Display?> =
+ displays
+ .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
+ .flatMapLatest { it.asFlow() }
+
val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index aa1873c7ad41..162047bb3b79 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -135,6 +135,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
@@ -248,7 +249,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final IStatusBarService mStatusBarService;
protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
- private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
private final RingerModeTracker mRingerModeTracker;
@@ -364,7 +365,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
IStatusBarService statusBarService,
LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
- StatusBarWindowController statusBarWindowController,
+ StatusBarWindowControllerStore statusBarWindowControllerStore,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
@@ -400,7 +401,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mStatusBarService = statusBarService;
mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
- mStatusBarWindowController = statusBarWindowController;
+ mStatusBarWindowControllerStore = statusBarWindowControllerStore;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
mRingerModeTracker = ringerModeTracker;
@@ -708,7 +709,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mLightBarController,
mKeyguardStateController,
mNotificationShadeWindowController,
- mStatusBarWindowController,
+ mStatusBarWindowControllerStore.getDefaultDisplay(),
this::onRefresh,
mKeyguardShowing,
mPowerAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 063adc834f30..3230285fcd71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -30,8 +30,6 @@ import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.internal.jank.InteractionJankMonitor
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
-import com.android.keyguard.LegacyLockIconViewController
-import com.android.keyguard.LockIconView
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
@@ -39,7 +37,6 @@ import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
@@ -94,7 +91,6 @@ constructor(
private val configuration: ConfigurationState,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
- private val lockIconViewController: Lazy<LegacyLockIconViewController>,
private val shadeInteractor: ShadeInteractor,
private val interactionJankMonitor: InteractionJankMonitor,
private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
@@ -171,10 +167,6 @@ constructor(
private fun initializeViews() {
val indicationArea = KeyguardIndicationArea(context, null)
keyguardIndicationController.setIndicationArea(indicationArea)
-
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
- lockIconViewController.get().setLockIconView(LockIconView(context, null))
- }
}
private fun bindKeyguardRootView() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d28b08f83a4e..fbc76c587be2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -139,7 +139,6 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.ui.viewmodel.DreamViewModel;
import com.android.systemui.dump.DumpManager;
@@ -3569,9 +3568,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
// Ensure that keyguard becomes visible if the going away animation is canceled
- if (showKeyguard && !KeyguardWmStateRefactor.isEnabled()
- && (MigrateClocksToBlueprint.isEnabled()
- || DeviceEntryUdfpsRefactor.isEnabled())) {
+ if (showKeyguard && !KeyguardWmStateRefactor.isEnabled()) {
mKeyguardInteractor.showKeyguard();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index fb9719142b54..7ca2c2004452 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -22,7 +22,6 @@ import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -34,13 +33,7 @@ object AlternateBouncerUdfpsViewBinder {
/** Updates UI for the UDFPS icon on the alternate bouncer. */
@JvmStatic
- fun bind(
- view: DeviceEntryIconView,
- viewModel: AlternateBouncerUdfpsIconViewModel,
- ) {
- if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
- return
- }
+ fun bind(view: DeviceEntryIconView, viewModel: AlternateBouncerUdfpsIconViewModel) {
val fgIconView = view.iconView
val bgView = view.bgView
@@ -66,7 +59,7 @@ object AlternateBouncerUdfpsViewBinder {
viewModel.fgViewModel.collect { fgViewModel ->
fgIconView.setImageState(
view.getIconState(fgViewModel.type, fgViewModel.useAodVariant),
- /* merge */ false
+ /* merge */ false,
)
fgIconView.imageTintList = ColorStateList.valueOf(fgViewModel.tint)
fgIconView.setPadding(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 76962732ad01..1891af2d04b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -33,7 +33,6 @@ import com.android.app.tracing.coroutines.launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
@@ -95,7 +94,7 @@ constructor(
private var alternateBouncerView: ConstraintLayout? = null
override fun start() {
- if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) {
+ if (SceneContainerFlag.isEnabled) {
return
}
@@ -182,14 +181,7 @@ constructor(
}
/** Binds the view to the view-model, continuing to update the former based on the latter. */
- fun bind(
- view: ConstraintLayout,
- alternateBouncerDependencies: AlternateBouncerDependencies,
- ) {
- if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
- return
- }
-
+ fun bind(view: ConstraintLayout, alternateBouncerDependencies: AlternateBouncerDependencies) {
optionallyAddUdfpsViews(
view = view,
logger = alternateBouncerDependencies.logger,
@@ -287,10 +279,7 @@ constructor(
)
}
view.addView(udfpsView)
- AlternateBouncerUdfpsViewBinder.bind(
- udfpsView,
- udfpsIconViewModel,
- )
+ AlternateBouncerUdfpsViewBinder.bind(udfpsView, udfpsIconViewModel)
}
val constraintSet = ConstraintSet().apply { clone(view) }
@@ -310,17 +299,17 @@ constructor(
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- iconLocation.left
+ iconLocation.left,
)
// udfpsA11yOverlayView:
constrainWidth(
udfpsA11yOverlayViewId,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
constrainHeight(
udfpsA11yOverlayViewId,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
}
constraintSet.applyTo(view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index b951b736abf2..a3f3342b9a6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -30,7 +30,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
import com.android.systemui.common.ui.view.LongPressHandlingView
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
@@ -68,7 +67,6 @@ object DeviceEntryIconViewBinder {
vibratorHelper: VibratorHelper,
overrideColor: Color? = null,
): DisposableHandle {
- DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val disposables = DisposableHandles()
val longPressHandlingView = view.longPressHandlingView
val fgIconView = view.iconView
@@ -79,7 +77,7 @@ object DeviceEntryIconViewBinder {
view: View,
x: Int,
y: Int,
- isA11yAction: Boolean
+ isA11yAction: Boolean,
) {
if (
!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
@@ -87,14 +85,11 @@ object DeviceEntryIconViewBinder {
Log.d(
TAG,
"Long press rejected because it is not a11yAction " +
- "and it is a falseLongTap"
+ "and it is a falseLongTap",
)
return
}
- vibratorHelper.performHapticFeedback(
- view,
- HapticFeedbackConstants.CONFIRM,
- )
+ vibratorHelper.performHapticFeedback(view, HapticFeedbackConstants.CONFIRM)
applicationScope.launch {
view.clearFocus()
view.clearAccessibilityFocus()
@@ -192,7 +187,7 @@ object DeviceEntryIconViewBinder {
fgViewModel.viewModel.collect { viewModel ->
fgIconView.setImageState(
view.getIconState(viewModel.type, viewModel.useAodVariant),
- /* merge */ false
+ /* merge */ false,
)
if (viewModel.type.contentDescriptionResId != -1) {
fgIconView.contentDescription =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 00aa44fe795b..0470e086ad27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -23,6 +23,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition
@@ -32,7 +33,6 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.util.kotlin.pairwise
@@ -128,7 +128,7 @@ object KeyguardBlueprintViewBinder {
) {
val currentClock = viewModel.currentClock.value
if (!DEBUG || currentClock == null) return
- val smallClockViewId = R.id.lockscreen_clock_view
+ val smallClockViewId = customR.id.lockscreen_clock_view
val largeClockViewId = currentClock.largeClock.layout.views[0].id
val smartspaceDateId = sharedR.id.date_smartspace_view
Log.i(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 57cb10ff9367..2d225562081a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -35,7 +35,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
import com.android.internal.policy.SystemBarUtils
-import com.android.systemui.customization.R as customizationR
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen
@@ -50,6 +50,8 @@ import kotlin.reflect.KSuspendFunction1
/** Binder for the small clock view, large clock view. */
object KeyguardPreviewClockViewBinder {
+ val lockId = View.generateViewId()
+
@JvmStatic
fun bind(
largeClockHostView: View,
@@ -120,36 +122,36 @@ object KeyguardPreviewClockViewBinder {
private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) {
constraints.apply {
- constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
+ constrainWidth(customR.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
// The following two lines make lockscreen_clock_view_large is constrained to available
// height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
- constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
- constrainMaxHeight(R.id.lockscreen_clock_view_large, 0)
+ constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
+ constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
val largeClockTopMargin =
SystemBarUtils.getStatusBarHeight(context) +
context.resources.getDimensionPixelSize(
- customizationR.dimen.small_clock_padding_top
+ customR.dimen.small_clock_padding_top
) +
context.resources.getDimensionPixelSize(
R.dimen.keyguard_smartspace_top_offset
) +
getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
- connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
- connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
+ connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
connect(
- R.id.lockscreen_clock_view_large,
+ customR.id.lockscreen_clock_view_large,
ConstraintSet.END,
PARENT_ID,
ConstraintSet.END,
)
- // In preview, we'll show UDFPS icon for UDFPS devices
- // and nothing for non-UDFPS devices,
- // but we need position of device entry icon to constrain clock
- if (getConstraint(R.id.lock_icon_view) != null) {
- connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP)
- } else {
+
+ // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
+ // devices, but we need position of device entry icon to constrain clock
+ if (getConstraint(lockId) != null) {
+ connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
+ } else {
// Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
val bottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
@@ -159,7 +161,7 @@ object KeyguardPreviewClockViewBinder {
val lockIconRadiusPx = (defaultDensity * 36).toInt()
val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
connect(
- R.id.lockscreen_clock_view_large,
+ customR.id.lockscreen_clock_view_large,
BOTTOM,
PARENT_ID,
BOTTOM,
@@ -167,23 +169,23 @@ object KeyguardPreviewClockViewBinder {
)
}
- constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
+ constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
constrainHeight(
- R.id.lockscreen_clock_view,
- context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height),
+ customR.id.lockscreen_clock_view,
+ context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
)
connect(
- R.id.lockscreen_clock_view,
+ customR.id.lockscreen_clock_view,
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) +
+ context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
val smallClockTopMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
Utils.getStatusBarHeaderHeightKeyguard(context)
- connect(R.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin)
+ connect(customR.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ee2ee522cd0e..ea70fd02eed5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -52,8 +52,8 @@ import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.onApplyWindowInsets
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.common.ui.view.onTouchListener
+import com.android.systemui.customization.R as customR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -180,16 +180,12 @@ object KeyguardRootViewBinder {
}
}
- if (
- KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
- ) {
- launch("$TAG#alpha") {
- viewModel.alpha(viewState).collect { alpha ->
- view.alpha = alpha
- if (KeyguardBottomAreaRefactor.isEnabled) {
- childViews[statusViewId]?.alpha = alpha
- childViews[burnInLayerId]?.alpha = alpha
- }
+ launch("$TAG#alpha") {
+ viewModel.alpha(viewState).collect { alpha ->
+ view.alpha = alpha
+ if (KeyguardBottomAreaRefactor.isEnabled) {
+ childViews[statusViewId]?.alpha = alpha
+ childViews[burnInLayerId]?.alpha = alpha
}
}
}
@@ -223,7 +219,6 @@ object KeyguardRootViewBinder {
indicationArea,
startButton,
endButton,
- lockIcon,
deviceEntryIcon -> {
// Do not move these views
}
@@ -622,12 +617,11 @@ object KeyguardRootViewBinder {
private val statusViewId = R.id.keyguard_status_view
private val burnInLayerId = R.id.burn_in_layer
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
- private val largeClockId = R.id.lockscreen_clock_view_large
- private val smallClockId = R.id.lockscreen_clock_view
+ private val largeClockId = customR.id.lockscreen_clock_view_large
+ private val smallClockId = customR.id.lockscreen_clock_view
private val indicationArea = R.id.keyguard_indication_area
private val startButton = R.id.start_button
private val endButton = R.id.end_button
- private val lockIcon = R.id.lock_icon_view
private val deviceEntryIcon = R.id.device_entry_icon_view
private val nsslPlaceholderId = R.id.nssl_placeholder
private val authInteractionProperties = AuthInteractionProperties()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index cef9a4eaf2bd..dd8980dabb23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.preview
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.app.WallpaperColors
import android.content.BroadcastReceiver
import android.content.Context
@@ -48,6 +47,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isInvisible
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
@@ -151,10 +151,7 @@ constructor(
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
private val shouldHighlightSelectedAffordance: Boolean =
- bundle.getBoolean(
- KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
- false,
- )
+ bundle.getBoolean(KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES, false)
private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
private val display: Display? = displayManager.getDisplay(displayId)
@@ -188,24 +185,26 @@ constructor(
private var themeStyle: Style? = null
init {
- coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job() + createCoroutineTracingContext("KeyguardPreviewRenderer"))
+ coroutineScope =
+ CoroutineScope(
+ applicationScope.coroutineContext +
+ Job() +
+ createCoroutineTracingContext("KeyguardPreviewRenderer")
+ )
disposables += DisposableHandle { coroutineScope.cancel() }
clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
if (KeyguardBottomAreaRefactor.isEnabled) {
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ) ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+ ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
} else {
bottomAreaViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ),
+ bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
}
@@ -218,7 +217,7 @@ constructor(
context,
displayManager.getDisplay(DEFAULT_DISPLAY),
if (hostToken == null) null else InputTransferToken(hostToken),
- "KeyguardPreviewRenderer"
+ "KeyguardPreviewRenderer",
)
disposables += DisposableHandle { host.release() }
}
@@ -247,12 +246,12 @@ constructor(
rootView.measure(
View.MeasureSpec.makeMeasureSpec(
displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(),
- View.MeasureSpec.EXACTLY
+ View.MeasureSpec.EXACTLY,
),
View.MeasureSpec.makeMeasureSpec(
displayInfo?.logicalHeight
?: windowManager.currentWindowMetrics.bounds.height(),
- View.MeasureSpec.EXACTLY
+ View.MeasureSpec.EXACTLY,
),
)
rootView.layout(0, 0, rootView.measuredWidth, rootView.measuredHeight)
@@ -278,9 +277,7 @@ constructor(
}
}
- fun onStartCustomizingQuickAffordances(
- initiallySelectedSlotId: String?,
- ) {
+ fun onStartCustomizingQuickAffordances(initiallySelectedSlotId: String?) {
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId = initiallySelectedSlotId,
shouldHighlightSelectedAffordance = true,
@@ -379,15 +376,9 @@ constructor(
@Deprecated("Deprecated as part of b/278057014")
private fun setUpBottomArea(parentView: ViewGroup) {
val bottomAreaView =
- LayoutInflater.from(context)
- .inflate(
- R.layout.keyguard_bottom_area,
- parentView,
- false,
- ) as KeyguardBottomAreaView
- bottomAreaView.init(
- viewModel = bottomAreaViewModel,
- )
+ LayoutInflater.from(context).inflate(R.layout.keyguard_bottom_area, parentView, false)
+ as KeyguardBottomAreaView
+ bottomAreaView.init(viewModel = bottomAreaViewModel)
parentView.addView(
bottomAreaView,
FrameLayout.LayoutParams(
@@ -433,7 +424,7 @@ constructor(
setUpUdfps(
previewContext,
- if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView
+ if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
)
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -466,7 +457,7 @@ constructor(
previewContext,
it,
previewInSplitShade(),
- smartspaceViewModel
+ smartspaceViewModel,
)
}
setupCommunalTutorialIndicator(keyguardRootView)
@@ -515,23 +506,20 @@ constructor(
val finger =
LayoutInflater.from(previewContext)
- .inflate(
- R.layout.udfps_keyguard_preview,
- parentView,
- false,
- ) as View
+ .inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
// Place the UDFPS view in the proper sensor location
if (MigrateClocksToBlueprint.isEnabled) {
- finger.id = R.id.lock_icon_view
+ val lockId = KeyguardPreviewClockViewBinder.lockId
+ finger.id = lockId
parentView.addView(finger)
val cs = ConstraintSet()
cs.clone(parentView as ConstraintLayout)
cs.apply {
- constrainWidth(R.id.lock_icon_view, sensorBounds.width())
- constrainHeight(R.id.lock_icon_view, sensorBounds.height())
- connect(R.id.lock_icon_view, TOP, PARENT_ID, TOP, sensorBounds.top)
- connect(R.id.lock_icon_view, START, PARENT_ID, START, sensorBounds.left)
+ constrainWidth(lockId, sensorBounds.width())
+ constrainHeight(lockId, sensorBounds.height())
+ connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
+ connect(lockId, START, PARENT_ID, START, sensorBounds.left)
}
cs.applyTo(parentView)
} else {
@@ -541,7 +529,7 @@ constructor(
sensorBounds.left,
sensorBounds.top,
sensorBounds.right,
- sensorBounds.bottom
+ sensorBounds.bottom,
)
parentView.addView(finger, fingerprintLayoutParams)
}
@@ -565,7 +553,7 @@ constructor(
FrameLayout.LayoutParams.WRAP_CONTENT,
resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_height
- )
+ ),
)
layoutParams.topMargin =
SystemBarUtils.getStatusBarHeight(previewContext) +
@@ -579,7 +567,7 @@ constructor(
),
/* top = */ 0,
/* end = */ 0,
- /* bottom = */ 0
+ /* bottom = */ 0,
)
smallClockHostView.clipChildren = false
parentView.addView(smallClockHostView)
@@ -703,9 +691,7 @@ constructor(
private suspend fun fetchThemeStyleFromSetting(): Style {
val overlayPackageJson =
withContext(backgroundDispatcher) {
- secureSettings.getString(
- Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
- )
+ secureSettings.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)
}
return if (!overlayPackageJson.isNullOrEmpty()) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index d74552231209..ee4f41ddd5a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -30,7 +30,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.systemui.customization.R as custR
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -153,7 +153,7 @@ constructor(
R.id.weather_clock_bc_smartspace_bottom,
Barrier.BOTTOM,
getDimen(ENHANCED_SMARTSPACE_HEIGHT),
- (custR.id.weather_clock_time),
+ (customR.id.weather_clock_time),
)
if (
rootViewModel.isNotifIconContainerVisible.value.value &&
@@ -184,40 +184,40 @@ constructor(
if (keyguardClockViewModel.clockShouldBeCentered.value) PARENT_ID
else R.id.split_shade_guideline
constraints.apply {
- connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
- connect(R.id.lockscreen_clock_view_large, END, guideline, END)
- connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP)
+ connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
+ connect(customR.id.lockscreen_clock_view_large, END, guideline, END)
+ connect(customR.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP)
val largeClockTopMargin =
keyguardClockViewModel.getLargeClockTopMargin() +
getDimen(DATE_WEATHER_VIEW_HEIGHT) +
getDimen(ENHANCED_SMARTSPACE_HEIGHT)
- connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
- constrainWidth(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
+ connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
// The following two lines make lockscreen_clock_view_large is constrained to available
// height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
- constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
- constrainMaxHeight(R.id.lockscreen_clock_view_large, 0)
- constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
+ constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
+ constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
+ constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
constrainHeight(
- R.id.lockscreen_clock_view,
- context.resources.getDimensionPixelSize(custR.dimen.small_clock_height),
+ customR.id.lockscreen_clock_view,
+ context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
)
connect(
- R.id.lockscreen_clock_view,
+ customR.id.lockscreen_clock_view,
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(custR.dimen.clock_padding_start) +
+ context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
- connect(R.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
+ connect(customR.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
// Explicitly clear pivot to force recalculate pivot instead of using legacy value
- setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
+ setTransformPivot(customR.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
val smallClockBottom =
keyguardClockViewModel.getSmallClockTopMargin() +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 782d37b1929c..8d2bfb5bdf40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -27,11 +27,8 @@ import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.keyguard.LockIconView
-import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -66,7 +63,6 @@ constructor(
private val context: Context,
private val notificationPanelView: NotificationPanelView,
private val featureFlags: FeatureFlags,
- private val lockIconViewController: Lazy<LockIconViewController>,
private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
@@ -78,70 +74,44 @@ constructor(
private var disposableHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (
- !KeyguardBottomAreaRefactor.isEnabled &&
- !MigrateClocksToBlueprint.isEnabled &&
- !DeviceEntryUdfpsRefactor.isEnabled
- ) {
+ if (!KeyguardBottomAreaRefactor.isEnabled && !MigrateClocksToBlueprint.isEnabled) {
return
}
- notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
- notificationPanelView.removeView(it)
- }
-
val view =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(
- context,
- null,
- logger =
- LongPressHandlingViewLogger(
- logBuffer = logBuffer,
- TAG
- )
- )
- .apply { id = deviceEntryIconViewId }
- } else {
- // KeyguardBottomAreaRefactor.isEnabled or MigrateClocksToBlueprint.isEnabled
- LockIconView(context, null).apply { id = R.id.lock_icon_view }
- }
+ DeviceEntryIconView(
+ context,
+ null,
+ logger = LongPressHandlingViewLogger(logBuffer = logBuffer, TAG),
+ )
+ .apply { id = deviceEntryIconViewId }
+
constraintLayout.addView(view)
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
- disposableHandle?.dispose()
- disposableHandle =
- DeviceEntryIconViewBinder.bind(
- applicationScope,
- it,
- deviceEntryIconViewModel.get(),
- deviceEntryForegroundViewModel.get(),
- deviceEntryBackgroundViewModel.get(),
- falsingManager.get(),
- vibratorHelper.get(),
- )
- }
- } else {
- constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
- lockIconViewController.get().setLockIconView(it)
- }
+ constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
+ disposableHandle?.dispose()
+ disposableHandle =
+ DeviceEntryIconViewBinder.bind(
+ applicationScope,
+ it,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ )
}
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- val isUdfpsSupported =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- Log.d(
- "DefaultDeviceEntrySection",
- "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}"
- )
- deviceEntryIconViewModel.get().isUdfpsSupported.value
- } else {
- authController.isUdfpsSupported
- }
+ Log.d(
+ "DefaultDeviceEntrySection",
+ "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}",
+ )
+ val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
@@ -160,31 +130,24 @@ constructor(
val iconRadiusPx = (defaultDensity * 36).toInt()
if (isUdfpsSupported) {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
- Log.d(
- "DeviceEntrySection",
- "udfpsLocation=$udfpsLocation, " +
- "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
- "unusedAuthController=${authController.udfpsLocation}"
- )
- centerIcon(
- Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
- udfpsLocation.radius,
- constraintSet
- )
- }
- } else {
- authController.udfpsLocation?.let { udfpsLocation ->
- Log.d("DeviceEntrySection", "udfpsLocation=$udfpsLocation")
- centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
- }
+ deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
+ Log.d(
+ "DeviceEntrySection",
+ "udfpsLocation=$udfpsLocation, " +
+ "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
+ "unusedAuthController=${authController.udfpsLocation}",
+ )
+ centerIcon(
+ Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
+ udfpsLocation.radius,
+ constraintSet,
+ )
}
} else {
centerIcon(
Point(
(widthPixels / 2).toInt(),
- (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt()
+ (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt(),
),
iconRadiusPx * scaleFactor,
constraintSet,
@@ -193,12 +156,8 @@ constructor(
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- constraintLayout.removeView(deviceEntryIconViewId)
- disposableHandle?.dispose()
- } else {
- constraintLayout.removeView(R.id.lock_icon_view)
- }
+ constraintLayout.removeView(deviceEntryIconViewId)
+ disposableHandle?.dispose()
}
@VisibleForTesting
@@ -213,12 +172,7 @@ constructor(
)
}
- val iconId =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- deviceEntryIconViewId
- } else {
- R.id.lock_icon_view
- }
+ val iconId = deviceEntryIconViewId
constraintSet.apply {
constrainWidth(iconId, sensorRect.right - sensorRect.left)
@@ -228,14 +182,14 @@ constructor(
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP,
- sensorRect.top
+ sensorRect.top,
)
connect(
iconId,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
- sensorRect.left
+ sensorRect.left,
)
}
@@ -243,8 +197,8 @@ constructor(
// Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
// updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
// being in NPVC and laying out prior to the KeyguardRootView.
- // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled.
- if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) {
+ // Remove when KeyguardBottomAreaRefactor is enabled.
+ if (!KeyguardBottomAreaRefactor.isEnabled) {
with(notificationPanelView) {
val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
@@ -256,7 +210,7 @@ constructor(
ambientLeft,
sensorRect.bottom,
bottomAreaViewRight - ambientLeft,
- ambientTop + it.measuredHeight
+ ambientTop + it.measuredHeight,
)
} else {
// make bottom of ambient indication view the top of the lock icon
@@ -264,7 +218,7 @@ constructor(
ambientLeft,
sensorRect.top - it.measuredHeight,
bottomAreaViewRight - ambientLeft,
- sensorRect.top
+ sensorRect.top,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
index b33d55244037..604318a2751c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
@@ -22,6 +22,7 @@ import android.view.ViewGroup
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
@@ -67,7 +68,7 @@ constructor(
connect(
R.id.keyguard_slice_view,
ConstraintSet.TOP,
- R.id.lockscreen_clock_view,
+ customR.id.lockscreen_clock_view,
ConstraintSet.BOTTOM
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index edcf97a81ea4..620cc13a0c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -55,11 +55,7 @@ constructor(
R.id.nssl_placeholder_barrier_bottom,
Barrier.TOP,
0,
- *intArrayOf(
- R.id.device_entry_icon_view,
- R.id.lock_icon_view,
- R.id.ambient_indication_container
- )
+ *intArrayOf(R.id.device_entry_icon_view, R.id.ambient_indication_container),
)
connect(placeHolderId, BOTTOM, R.id.nssl_placeholder_barrier_bottom, TOP)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 99160f8a9158..6ddcae38ce92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -23,6 +23,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -157,7 +158,7 @@ constructor(
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.TOP,
- R.id.lockscreen_clock_view,
+ customR.id.lockscreen_clock_view,
ConstraintSet.BOTTOM
)
connect(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 4d914c721d0c..c11005d38986 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -29,6 +29,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnPreDrawListener
import com.android.app.animation.Interpolators
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
@@ -242,11 +243,11 @@ class ClockSizeTransition(
}
?: run {
Log.e(TAG, "No large clock set, falling back")
- addTarget(R.id.lockscreen_clock_view_large)
+ addTarget(customR.id.lockscreen_clock_view_large)
}
} else {
if (DEBUG) Log.i(TAG, "Adding small clock")
- addTarget(R.id.lockscreen_clock_view)
+ addTarget(customR.id.lockscreen_clock_view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
index 26b2e2b7bd66..424be90ba2ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
@@ -38,6 +38,6 @@ constructor(
) {
val rows =
configurationRepository.onConfigurationChange.emitOnStart().map {
- resources.getInteger(R.integer.quick_settings_max_rows)
+ resources.getInteger(R.integer.quick_settings_paginated_grid_num_rows)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
index f7c71ceb9e6c..ee0cfb304db0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
@@ -34,6 +34,6 @@ constructor(
) {
val rows =
configurationRepository.onConfigurationChange.emitOnStart().map {
- resources.getInteger(R.integer.quick_qs_panel_max_rows)
+ resources.getInteger(R.integer.quick_qs_paginated_grid_num_rows)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
new file mode 100644
index 000000000000..b9994d7bb821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.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.qs.panels.ui.compose
+
+import com.android.compose.animation.Bounceable
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.ui.model.GridCell
+import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+
+data class BounceableInfo(
+ val bounceable: BounceableTileViewModel,
+ val previousTile: Bounceable?,
+ val nextTile: Bounceable?,
+ val bounceEnd: Boolean,
+)
+
+fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo(
+ index: Int,
+ columns: Int,
+): BounceableInfo {
+ val cell = this[index].first as TileGridCell
+ // Only look for neighbor bounceables if they are on the same row
+ val onLastColumn = cell.onLastColumn(cell.column, columns)
+ val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 }
+ val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+ return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn)
+}
+
+fun List<BounceableTileViewModel>.bounceableInfo(
+ sizedTile: SizedTile<TileViewModel>,
+ index: Int,
+ column: Int,
+ columns: Int,
+): BounceableInfo {
+ // Only look for neighbor bounceables if they are on the same row
+ val onLastColumn = sizedTile.onLastColumn(column, columns)
+ val previousTile = getOrNull(index - 1)?.takeIf { column != 0 }
+ val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+ return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn)
+}
+
+private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean {
+ return column == columns - width
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 770fd785723a..74fa0fef21d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -116,9 +116,9 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c
regenerateGrid()
_tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
} else {
- // Add the tile with a temporary row which will get reassigned when
+ // Add the tile with a temporary row/col which will get reassigned when
// regenerating spacers
- _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0))
+ _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0))
}
regenerateGrid()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index a645b51404e7..f36f45c7942d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
@@ -29,6 +31,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
import com.android.systemui.res.R
@@ -41,7 +44,9 @@ fun SceneScope.QuickQuickSettings(
val sizedTiles by
viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
val tiles = sizedTiles.fastMap { it.tile }
+ val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+ val scope = rememberCoroutineScope()
DisposableEffect(tiles) {
val token = Any()
@@ -49,6 +54,7 @@ fun SceneScope.QuickQuickSettings(
onDispose { tiles.forEach { it.stopListening(token) } }
}
val columns by viewModel.columns.collectAsStateWithLifecycle()
+ var cellIndex = 0
Box(modifier = modifier) {
GridAnchor()
VerticalSpannedGrid(
@@ -59,11 +65,15 @@ fun SceneScope.QuickQuickSettings(
modifier = Modifier.sysuiResTag("qqs_tile_layout"),
) { spanIndex ->
val it = sizedTiles[spanIndex]
+ val column = cellIndex % columns
+ cellIndex += it.width
Tile(
tile = it.tile,
iconOnly = it.isIcon,
modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 9ec5a82a18d7..71fa0ac30fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -54,6 +55,7 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.background
import com.android.compose.modifiers.thenIf
@@ -64,7 +66,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
import com.android.systemui.res.R
-import kotlinx.coroutines.delay
private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
@@ -138,13 +139,20 @@ fun LargeTileLabels(
accessibilityUiState: AccessibilityUiState? = null,
) {
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
- Text(label, color = colors.label, modifier = Modifier.tileMarquee())
+ Text(
+ label,
+ style = MaterialTheme.typography.labelLarge,
+ color = colors.label,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
if (!TextUtils.isEmpty(secondaryLabel)) {
Text(
secondaryLabel ?: "",
color = colors.secondaryLabel,
+ style = MaterialTheme.typography.bodyMedium,
modifier =
- Modifier.tileMarquee().thenIf(
+ Modifier.thenIf(
accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") ==
true
) {
@@ -182,10 +190,7 @@ fun SmallTileContent(
rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
} else {
var atEnd by remember(icon.res) { mutableStateOf(false) }
- LaunchedEffect(key1 = icon.res) {
- delay(350)
- atEnd = true
- }
+ LaunchedEffect(key1 = icon.res) { atEnd = true }
rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 45c1e48840ee..5c2a2bd2b78c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -22,13 +22,13 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@@ -46,7 +46,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
@@ -66,19 +65,17 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
@@ -98,23 +95,26 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
-import androidx.compose.ui.zIndex
+import com.android.compose.animation.bounceable
import com.android.compose.modifiers.background
import com.android.compose.modifiers.height
import com.android.systemui.common.ui.compose.load
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
import com.android.systemui.qs.panels.ui.compose.DragAndDropState
import com.android.systemui.qs.panels.ui.compose.EditTileListState
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
-import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle
+import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer
import com.android.systemui.qs.panels.ui.compose.selection.TileWidths
import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile
import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
@@ -122,11 +122,14 @@ import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.res.R
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
object TileType
@@ -241,15 +244,20 @@ private fun CurrentTilesGrid(
onSetTiles: (List<TileSpec>) -> Unit,
) {
val currentListState by rememberUpdatedState(listState)
- val tileHeight = CommonTileDefaults.TileHeight
val totalRows = listState.tiles.lastOrNull()?.row ?: 0
val totalHeight by
animateDpAsState(
- gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding),
+ gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding),
label = "QSEditCurrentTilesGridHeight",
)
val gridState = rememberLazyGridState()
var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
+ val coroutineScope = rememberCoroutineScope()
+
+ val cells =
+ remember(listState.tiles) {
+ listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
+ }
TileLazyGrid(
state = gridState,
@@ -272,7 +280,7 @@ private fun CurrentTilesGrid(
}
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
- EditTiles(listState.tiles, listState, selectionState) { spec ->
+ EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec ->
// Toggle the current size of the tile
currentListState.isIcon(spec)?.let { onResize(spec, !it) }
}
@@ -286,10 +294,10 @@ private fun AvailableTileGrid(
columns: Int,
dragAndDropState: DragAndDropState,
) {
- // Available tiles aren't visible during drag and drop, so the row isn't needed
+ // Available tiles aren't visible during drag and drop, so the row/col isn't needed
val groupedTiles =
remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
- groupAndSort(tiles.fastMap { TileGridCell(it, 0) })
+ groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) })
}
val labelColors = EditModeTileDefaults.editTileColors()
@@ -349,24 +357,26 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
/**
* Adds a list of [GridCell] to the lazy grid
*
- * @param cells the list of [GridCell]
+ * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
* @param dragAndDropState the [DragAndDropState] for this grid
* @param selectionState the [MutableSelectionState] for this grid
* @param onToggleSize the callback when a tile's size is toggled
*/
fun LazyGridScope.EditTiles(
- cells: List<GridCell>,
+ cells: List<Pair<GridCell, BounceableTileViewModel>>,
+ columns: Int,
dragAndDropState: DragAndDropState,
selectionState: MutableSelectionState,
+ coroutineScope: CoroutineScope,
onToggleSize: (spec: TileSpec) -> Unit,
) {
items(
count = cells.size,
- key = { cells[it].key(it, dragAndDropState) },
- span = { cells[it].span },
+ key = { cells[it].first.key(it, dragAndDropState) },
+ span = { cells[it].first.span },
contentType = { TileType },
) { index ->
- when (val cell = cells[index]) {
+ when (val cell = cells[index].first) {
is TileGridCell ->
if (dragAndDropState.isMoving(cell.tile.tileSpec)) {
// If the tile is being moved, replace it with a visible spacer
@@ -385,6 +395,9 @@ fun LazyGridScope.EditTiles(
dragAndDropState = dragAndDropState,
selectionState = selectionState,
onToggleSize = onToggleSize,
+ coroutineScope = coroutineScope,
+ bounceableInfo = cells.bounceableInfo(index, columns),
+ modifier = Modifier.animateItem(),
)
}
is SpacerGridCell -> SpacerGridCell()
@@ -393,12 +406,15 @@ fun LazyGridScope.EditTiles(
}
@Composable
-private fun LazyGridItemScope.TileGridCell(
+private fun TileGridCell(
cell: TileGridCell,
index: Int,
dragAndDropState: DragAndDropState,
selectionState: MutableSelectionState,
onToggleSize: (spec: TileSpec) -> Unit,
+ coroutineScope: CoroutineScope,
+ bounceableInfo: BounceableInfo,
+ modifier: Modifier = Modifier,
) {
val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
var selected by remember { mutableStateOf(false) }
@@ -407,6 +423,9 @@ private fun LazyGridItemScope.TileGridCell(
targetValue = if (selected) 1f else 0f,
label = "QSEditTileSelectionAlpha",
)
+ val selectionColor = MaterialTheme.colorScheme.primary
+ val colors = EditModeTileDefaults.editTileColors()
+ val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
LaunchedEffect(selectionState.selection?.tileSpec) {
selectionState.selection?.let {
@@ -420,152 +439,61 @@ private fun LazyGridItemScope.TileGridCell(
selected = selectionState.selection?.tileSpec == cell.tile.tileSpec
}
- val modifier =
- Modifier.animateItem()
- .semantics(mergeDescendants = true) {
- this.stateDescription = stateDescription
- contentDescription = cell.tile.label.text
- customActions =
- listOf(
- // TODO(b/367748260): Add final accessibility actions
- CustomAccessibilityAction("Toggle size") {
- onToggleSize(cell.tile.tileSpec)
- true
- }
- )
- }
- .height(CommonTileDefaults.TileHeight)
- .fillMaxWidth()
-
- val content =
- @Composable {
- EditTile(
- tileViewModel = cell.tile,
- iconOnly = cell.isIcon,
- selectionAlpha = { selectionAlpha },
- modifier =
- Modifier.fillMaxSize()
- .selectableTile(cell.tile.tileSpec, selectionState)
- .dragAndDropTileSource(
- SizedTileImpl(cell.tile, cell.width),
- dragAndDropState,
- selectionState::unSelect,
- ),
- )
- }
-
- if (selected) {
- SelectedTile(
- isIcon = cell.isIcon,
- selectionAlpha = { selectionAlpha },
- selectionState = selectionState,
- modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged
- content = content,
- )
- } else {
- UnselectedTile(
- selectionAlpha = { selectionAlpha },
- selectionState = selectionState,
- modifier = modifier,
- content = content,
- )
- }
-}
-
-@Composable
-private fun SelectedTile(
- isIcon: Boolean,
- selectionAlpha: () -> Float,
- selectionState: MutableSelectionState,
- modifier: Modifier = Modifier,
- content: @Composable () -> Unit,
-) {
// Current base, min and max width of this tile
var tileWidths: TileWidths? by remember { mutableStateOf(null) }
-
- // Animated diff between the current width and the resized width of the tile. We can't use
- // animateContentSize here as the tile is sometimes unbounded.
- val remainingOffset by
- animateIntAsState(
- selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0,
- label = "QSEditTileWidthOffset",
- )
-
val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() }
- Box(
- modifier.onSizeChanged {
- val min = if (isIcon) it.width else (it.width - padding) / 2
- val max = if (isIcon) (it.width * 2) + padding else it.width
- tileWidths = TileWidths(it.width, min, max)
- }
+
+ ResizableTileContainer(
+ selected = selected,
+ selectionState = selectionState,
+ selectionAlpha = { selectionAlpha },
+ selectionColor = selectionColor,
+ tileWidths = { tileWidths },
+ modifier =
+ modifier
+ .height(TileHeight)
+ .fillMaxWidth()
+ .onSizeChanged {
+ // Grab the size before the bounceable to get the idle width
+ val min = if (cell.isIcon) it.width else (it.width - padding) / 2
+ val max = if (cell.isIcon) (it.width * 2) + padding else it.width
+ tileWidths = TileWidths(it.width, min, max)
+ }
+ .bounceable(
+ bounceable = currentBounceableInfo.bounceable,
+ previousBounceable = currentBounceableInfo.previousTile,
+ nextBounceable = currentBounceableInfo.nextTile,
+ orientation = Orientation.Horizontal,
+ bounceEnd = currentBounceableInfo.bounceEnd,
+ ),
) {
- val handle =
- @Composable {
- ResizingHandle(
- enabled = true,
- selectionState = selectionState,
- transition = selectionAlpha,
- tileWidths = { tileWidths },
+ Box(
+ modifier
+ .fillMaxSize()
+ .semantics(mergeDescendants = true) {
+ this.stateDescription = stateDescription
+ contentDescription = cell.tile.label.text
+ customActions =
+ listOf(
+ // TODO(b/367748260): Add final accessibility actions
+ CustomAccessibilityAction("Toggle size") {
+ onToggleSize(cell.tile.tileSpec)
+ true
+ }
+ )
+ }
+ .selectableTile(cell.tile.tileSpec, selectionState) {
+ coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+ }
+ .dragAndDropTileSource(
+ SizedTileImpl(cell.tile, cell.width),
+ dragAndDropState,
+ selectionState::unSelect,
)
- }
-
- Layout(contents = listOf(content, handle)) {
- (contentMeasurables, handleMeasurables),
- constraints ->
- // Grab the width from the resizing state if a resize is in progress, otherwise fill the
- // max width
- val width =
- selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
- val contentPlaceable =
- contentMeasurables.first().measure(constraints.copy(maxWidth = width))
- val handlePlaceable = handleMeasurables.first().measure(constraints)
-
- // Place the dot vertically centered on the right edge
- val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
- val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- contentPlaceable.place(0, 0)
- handlePlaceable.place(handleX, handleY)
- }
- }
- }
-}
-
-@Composable
-private fun UnselectedTile(
- selectionAlpha: () -> Float,
- selectionState: MutableSelectionState,
- modifier: Modifier = Modifier,
- content: @Composable () -> Unit,
-) {
- val handle =
- @Composable {
- ResizingHandle(
- enabled = false,
- selectionState = selectionState,
- transition = selectionAlpha,
- )
- }
-
- Box(modifier) {
- Layout(contents = listOf(content, handle)) {
- (contentMeasurables, handleMeasurables),
- constraints ->
- val contentPlaceable =
- contentMeasurables
- .first()
- .measure(constraints.copy(maxWidth = constraints.maxWidth))
- val handlePlaceable = handleMeasurables.first().measure(constraints)
-
- // Place the dot vertically centered on the right edge
- val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
- val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- contentPlaceable.place(0, 0)
- handlePlaceable.place(handleX, handleY)
- }
+ .tileBackground(colors.background)
+ .tilePadding()
+ ) {
+ EditTile(tile = cell.tile, iconOnly = cell.isIcon)
}
}
}
@@ -588,19 +516,19 @@ private fun AvailableTileGridCell(
verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top),
modifier = modifier,
) {
- EditTileContainer(
- colors = colors,
- modifier =
- Modifier.fillMaxWidth()
- .height(CommonTileDefaults.TileHeight)
- .clearSelectionTile(selectionState)
- .semantics(mergeDescendants = true) {
- onClick(onClickActionName) { false }
- this.stateDescription = stateDescription
- }
- .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
- selectionState.unSelect()
- },
+ Box(
+ Modifier.fillMaxWidth()
+ .height(TileHeight)
+ .clearSelectionTile(selectionState)
+ .semantics(mergeDescendants = true) {
+ onClick(onClickActionName) { false }
+ this.stateDescription = stateDescription
+ }
+ .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
+ selectionState.unSelect()
+ }
+ .tileBackground(colors.background)
+ .tilePadding()
) {
// Icon
SmallTileContent(
@@ -626,16 +554,14 @@ private fun AvailableTileGridCell(
@Composable
private fun SpacerGridCell(modifier: Modifier = Modifier) {
// By default, spacers are invisible and exist purely to catch drag movements
- Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth())
+ Box(modifier.height(TileHeight).fillMaxWidth())
}
@Composable
-fun EditTile(
- tileViewModel: EditTileViewModel,
+fun BoxScope.EditTile(
+ tile: EditTileViewModel,
iconOnly: Boolean,
- modifier: Modifier = Modifier,
colors: TileColors = EditModeTileDefaults.editTileColors(),
- selectionAlpha: () -> Float = { 1f },
) {
// Animated horizontal alignment from center (0f) to start (-1f)
val alignmentValue by
@@ -646,68 +572,36 @@ fun EditTile(
val alignment by remember {
derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) }
}
+ // Icon
+ Box(Modifier.size(ToggleTargetSize).align(alignment)) {
+ SmallTileContent(
+ icon = tile.icon,
+ color = colors.icon,
+ animateToEnd = true,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
- EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) {
- // Icon
- Box(Modifier.size(ToggleTargetSize).align(alignment)) {
- SmallTileContent(
- icon = tileViewModel.icon,
- color = colors.icon,
- animateToEnd = true,
- modifier = Modifier.align(Alignment.Center),
- )
- }
-
- // Labels, positioned after the icon
- AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
- LargeTileLabels(
- label = tileViewModel.label.text,
- secondaryLabel = tileViewModel.appName?.text,
- colors = colors,
- modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
- )
- }
+ // Labels, positioned after the icon
+ AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
+ LargeTileLabels(
+ label = tile.label.text,
+ secondaryLabel = tile.appName?.text,
+ colors = colors,
+ modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
+ )
}
}
-@Composable
-private fun EditTileContainer(
- colors: TileColors,
- modifier: Modifier = Modifier,
- selectionAlpha: () -> Float = { 0f },
- selectionColor: Color = MaterialTheme.colorScheme.primary,
- content: @Composable BoxScope.() -> Unit = {},
-) {
- Box(
- Modifier.wrapContentSize().drawWithContent {
- drawContent()
- drawRoundRect(
- SolidColor(selectionColor),
- cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
- style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()),
- alpha = selectionAlpha(),
- )
- }
- ) {
- Box(
- modifier =
- modifier
- .drawBehind {
- drawRoundRect(
- SolidColor(colors.background),
- cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
- )
- }
- .tilePadding(),
- content = content,
- )
+private fun Modifier.tileBackground(color: Color): Modifier {
+ return drawBehind {
+ drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx()))
}
}
private object EditModeTileDefaults {
const val PLACEHOLDER_ALPHA = .3f
val EditGridHeaderHeight = 60.dp
- val SelectedBorderWidth = 2.dp
val CurrentTilesGridPadding = 8.dp
@Composable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 6920e498bdde..e5c213519415 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
@@ -29,7 +30,9 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
import com.android.systemui.qs.panels.ui.compose.rememberEditListState
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
@@ -62,7 +65,11 @@ constructor(
}
val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
+ val bounceables =
+ remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+ val scope = rememberCoroutineScope()
+ var cellIndex = 0
VerticalSpannedGrid(
columns = columns,
@@ -71,11 +78,15 @@ constructor(
spans = sizedTiles.fastMap { it.width },
) { spanIndex ->
val it = sizedTiles[spanIndex]
+ val column = cellIndex % columns
+ cellIndex += it.width
Tile(
tile = it.tile,
iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 4bd5b2d68c4c..52d526123430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -23,8 +23,8 @@ import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
@@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -61,11 +62,13 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.compose.animation.bounceable
import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -74,6 +77,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.toUiState
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
private const val TEST_TAG_SMALL = "qs_tile_small"
private const val TEST_TAG_LARGE = "qs_tile_large"
@@ -102,9 +107,12 @@ fun Tile(
tile: TileViewModel,
iconOnly: Boolean,
squishiness: () -> Float,
+ coroutineScope: CoroutineScope,
+ bounceableInfo: BounceableInfo,
modifier: Modifier = Modifier,
) {
val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
+ val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
val colors = TileDefaults.getColorForState(uiState)
@@ -112,7 +120,7 @@ fun Tile(
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape = TileDefaults.animateTileShape(uiState.state)
- TileContainer(
+ TileExpandable(
color =
if (iconOnly || !uiState.handlesSecondaryClick) {
colors.iconBackground
@@ -120,93 +128,101 @@ fun Tile(
colors.background
},
shape = tileShape,
- iconOnly = iconOnly,
- onClick = tile::onClick,
- onLongClick = tile::onLongClick,
- uiState = uiState,
squishiness = squishiness,
- modifier = modifier,
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .bounceable(
+ bounceable = currentBounceableInfo.bounceable,
+ previousBounceable = currentBounceableInfo.previousTile,
+ nextBounceable = currentBounceableInfo.nextTile,
+ orientation = Orientation.Horizontal,
+ bounceEnd = currentBounceableInfo.bounceEnd,
+ ),
) { expandable ->
- val icon = getTileIcon(icon = uiState.icon)
- if (iconOnly) {
- SmallTileContent(
- icon = icon,
- color = colors.icon,
- modifier = Modifier.align(Alignment.Center),
- )
- } else {
- val iconShape = TileDefaults.animateIconShape(uiState.state)
- LargeTileContent(
- label = uiState.label,
- secondaryLabel = uiState.secondaryLabel,
- icon = icon,
- colors = colors,
- iconShape = iconShape,
- toggleClickSupported = state.handlesSecondaryClick,
- onClick = {
- if (state.handlesSecondaryClick) {
- tile.onSecondaryClick()
- }
- },
- onLongClick = { tile.onLongClick(expandable) },
- accessibilityUiState = uiState.accessibilityUiState,
- squishiness = squishiness,
- )
+ TileContainer(
+ onClick = {
+ tile.onClick(expandable)
+ if (uiState.accessibilityUiState.toggleableState != null) {
+ coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+ }
+ },
+ onLongClick = { tile.onLongClick(expandable) },
+ uiState = uiState,
+ iconOnly = iconOnly,
+ ) {
+ val icon = getTileIcon(icon = uiState.icon)
+ if (iconOnly) {
+ SmallTileContent(
+ icon = icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ } else {
+ val iconShape = TileDefaults.animateIconShape(uiState.state)
+ LargeTileContent(
+ label = uiState.label,
+ secondaryLabel = uiState.secondaryLabel,
+ icon = icon,
+ colors = colors,
+ iconShape = iconShape,
+ toggleClickSupported = state.handlesSecondaryClick,
+ onClick = {
+ if (state.handlesSecondaryClick) {
+ tile.onSecondaryClick()
+ }
+ },
+ onLongClick = { tile.onLongClick(expandable) },
+ accessibilityUiState = uiState.accessibilityUiState,
+ squishiness = squishiness,
+ )
+ }
}
}
}
@Composable
-private fun TileContainer(
+private fun TileExpandable(
color: Color,
shape: Shape,
- iconOnly: Boolean,
- uiState: TileUiState,
squishiness: () -> Float,
modifier: Modifier = Modifier,
- onClick: (Expandable) -> Unit = {},
- onLongClick: (Expandable) -> Unit = {},
- content: @Composable BoxScope.(Expandable) -> Unit,
+ content: @Composable (Expandable) -> Unit,
) {
Expandable(
color = color,
shape = shape,
modifier = modifier.clip(shape).verticalSquish(squishiness),
) {
- val longPressLabel = longPressLabel()
- Box(
- modifier =
- Modifier.height(CommonTileDefaults.TileHeight)
- .fillMaxWidth()
- .combinedClickable(
- onClick = { onClick(it) },
- onLongClick = { onLongClick(it) },
- onClickLabel = uiState.accessibilityUiState.clickLabel,
- onLongClickLabel = longPressLabel,
- )
- .semantics {
- role = uiState.accessibilityUiState.accessibilityRole
- if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
- uiState.accessibilityUiState.toggleableState?.let {
- toggleableState = it
- }
- }
- stateDescription = uiState.accessibilityUiState.stateDescription
- }
- .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
- .thenIf(iconOnly) {
- Modifier.semantics {
- contentDescription = uiState.accessibilityUiState.contentDescription
- }
- }
- .tilePadding()
- ) {
- content(it)
- }
+ content(it)
}
}
@Composable
+fun TileContainer(
+ onClick: () -> Unit,
+ onLongClick: () -> Unit,
+ uiState: TileUiState,
+ iconOnly: Boolean,
+ content: @Composable BoxScope.() -> Unit,
+) {
+ Box(
+ modifier =
+ Modifier.height(CommonTileDefaults.TileHeight)
+ .fillMaxWidth()
+ .tileCombinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ uiState = uiState,
+ iconOnly = iconOnly,
+ )
+ .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
+ .tilePadding(),
+ content = content,
+ )
+}
+
+@Composable
private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon {
val context = LocalContext.current
return icon.get()?.let {
@@ -222,14 +238,38 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal {
return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start)
}
-fun Modifier.tileMarquee(): Modifier {
- return basicMarquee(iterations = 1, initialDelayMillis = 200)
-}
-
fun Modifier.tilePadding(): Modifier {
return padding(CommonTileDefaults.TilePadding)
}
+@Composable
+fun Modifier.tileCombinedClickable(
+ onClick: () -> Unit,
+ onLongClick: () -> Unit,
+ uiState: TileUiState,
+ iconOnly: Boolean,
+): Modifier {
+ val longPressLabel = longPressLabel()
+ return combinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ onClickLabel = uiState.accessibilityUiState.clickLabel,
+ onLongClickLabel = longPressLabel,
+ )
+ .semantics {
+ role = uiState.accessibilityUiState.accessibilityRole
+ if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
+ uiState.accessibilityUiState.toggleableState?.let { toggleableState = it }
+ }
+ stateDescription = uiState.accessibilityUiState.stateDescription
+ }
+ .thenIf(iconOnly) {
+ Modifier.semantics {
+ contentDescription = uiState.accessibilityUiState.contentDescription
+ }
+ }
+}
+
data class TileColors(
val background: Color,
val iconBackground: Color,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
index 441d96289d86..1d36aee4eb85 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
@@ -89,8 +89,11 @@ class MutableSelectionState(
* Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as
* they can be selected.
*/
-@Composable
-fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier {
+fun Modifier.selectableTile(
+ tileSpec: TileSpec,
+ selectionState: MutableSelectionState,
+ onClick: () -> Unit = {},
+): Modifier {
return pointerInput(Unit) {
detectTapGestures(
onTap = {
@@ -99,6 +102,7 @@ fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelection
} else {
selectionState.select(tileSpec, manual = true)
}
+ onClick()
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index e0f0b6aa8919..9f13a3788f53 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -16,63 +16,117 @@
package com.android.systemui.qs.panels.ui.compose.selection
+import androidx.compose.animation.core.animateIntAsState
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.systemGestureExclusion
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.zIndex
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize
+import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth
/**
- * Dot handling resizing drag events. Use this on the selected tile to resize it
+ * Places a dot to handle resizing drag events. Use this on tiles to resize.
*
- * @param enabled whether resizing drag events should be handled
+ * The dot is placed vertically centered on the right border. The [content] will have a border when
+ * selected.
+ *
+ * @param selected whether resizing drag events should be handled
* @param selectionState the [MutableSelectionState] on the grid
- * @param transition the animated value for the dot, used for its alpha and scale
+ * @param selectionAlpha the animated value for the dot and border alpha
+ * @param selectionColor the [Color] of the dot and border
* @param tileWidths the [TileWidths] of the selected tile
- * @param onResize the callback when the drag passes the resizing threshold
*/
@Composable
-fun ResizingHandle(
+fun ResizableTileContainer(
+ selected: Boolean,
+ selectionState: MutableSelectionState,
+ selectionAlpha: () -> Float,
+ selectionColor: Color,
+ tileWidths: () -> TileWidths?,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit = {},
+) {
+ Box(
+ modifier
+ .resizable(selected, selectionState, tileWidths)
+ .selectionBorder(selectionColor, selectionAlpha)
+ ) {
+ content()
+ ResizingHandle(
+ enabled = selected,
+ selectionState = selectionState,
+ transition = selectionAlpha,
+ tileWidths = tileWidths,
+ modifier =
+ // Higher zIndex to make sure the handle is drawn above the content
+ Modifier.zIndex(2f),
+ )
+ }
+}
+
+@Composable
+private fun ResizingHandle(
enabled: Boolean,
selectionState: MutableSelectionState,
transition: () -> Float,
- tileWidths: () -> TileWidths? = { null },
+ tileWidths: () -> TileWidths?,
+ modifier: Modifier = Modifier,
) {
- if (enabled) {
- // Manually creating the touch target around the resizing dot to ensure that the next tile
- // does
- // not receive the touch input accidentally.
- val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
- Box(
- Modifier.size(minTouchTargetSize)
- .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
- .pointerInput(Unit) {
- detectHorizontalDragGestures(
- onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) },
- onDragStart = {
- tileWidths()?.let { selectionState.onResizingDragStart(it) }
- },
- onDragEnd = selectionState::onResizingDragEnd,
- onDragCancel = selectionState::onResizingDragEnd,
+ // Manually creating the touch target around the resizing dot to ensure that the next tile
+ // does not receive the touch input accidentally.
+ val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
+ Box(
+ modifier
+ .layout { measurable, constraints ->
+ val size = minTouchTargetSize.roundToPx()
+ val placeable = measurable.measure(Constraints(size, size, size, size))
+ layout(placeable.width, placeable.height) {
+ placeable.place(
+ x = constraints.maxWidth - placeable.width / 2,
+ y = constraints.maxHeight / 2 - placeable.height / 2,
)
}
- ) {
- ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
- }
- } else {
- ResizingDot(transition = transition)
+ }
+ .thenIf(enabled) {
+ Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
+ .pointerInput(Unit) {
+ detectHorizontalDragGestures(
+ onHorizontalDrag = { _, offset ->
+ selectionState.onResizingDrag(offset)
+ },
+ onDragStart = {
+ tileWidths()?.let { selectionState.onResizingDragStart(it) }
+ },
+ onDragEnd = selectionState::onResizingDragEnd,
+ onDragCancel = selectionState::onResizingDragEnd,
+ )
+ }
+ }
+ ) {
+ ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
}
}
@@ -88,6 +142,45 @@ private fun ResizingDot(
}
}
+private fun Modifier.selectionBorder(
+ selectionColor: Color,
+ selectionAlpha: () -> Float = { 0f },
+): Modifier {
+ return drawWithContent {
+ drawContent()
+ drawRoundRect(
+ SolidColor(selectionColor),
+ cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
+ style = Stroke(SelectedBorderWidth.toPx()),
+ alpha = selectionAlpha(),
+ )
+ }
+}
+
+@Composable
+private fun Modifier.resizable(
+ selected: Boolean,
+ selectionState: MutableSelectionState,
+ tileWidths: () -> TileWidths?,
+): Modifier {
+ if (!selected) return zIndex(1f)
+
+ // Animated diff between the current width and the resized width of the tile. We can't use
+ // animateContentSize here as the tile is sometimes unbounded.
+ val remainingOffset by
+ animateIntAsState(
+ selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0,
+ label = "QSEditTileWidthOffset",
+ )
+ return zIndex(2f).layout { measurable, constraints ->
+ // Grab the width from the resizing state if a resize is in progress
+ val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
+ val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width))
+ layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) }
+ }
+}
+
private object SelectionDefaults {
val ResizingDotSize = 16.dp
+ val SelectedBorderWidth = 2.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index b1841c4c5ffa..c0441f8a38a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -31,8 +31,8 @@ sealed interface GridCell {
}
/**
- * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's
- * positioned at
+ * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column
+ * it's positioned at
*/
@Immutable
data class TileGridCell(
@@ -41,13 +41,15 @@ data class TileGridCell(
override val width: Int,
override val span: GridItemSpan = GridItemSpan(width),
override val s: String = "${tile.tileSpec.spec}-$row-$width",
+ val column: Int,
) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
val key: String = "${tile.tileSpec.spec}-$row"
constructor(
sizedTile: SizedTile<EditTileViewModel>,
row: Int,
- ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width)
+ column: Int,
+ ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width)
}
/** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
@@ -73,7 +75,13 @@ fun List<SizedTile<EditTileViewModel>>.toGridCells(
return splitInRowsSequence(this, columns)
.flatMapIndexed { rowIndex, sizedTiles ->
val correctedRowIndex = rowIndex + startingRow
- val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) }
+ var column = 0
+ val row: List<GridCell> =
+ sizedTiles.map {
+ TileGridCell(it, correctedRowIndex, column).also { cell ->
+ column += cell.width
+ }
+ }
// Fill the incomplete rows with spacers
val numSpacers = columns - sizedTiles.sumOf { it.width }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
new file mode 100644
index 000000000000..506b05256880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.qs.panels.ui.viewmodel
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Bounceable
+
+class BounceableTileViewModel : Bounceable {
+ private val animatableBounce = Animatable(0.dp, Dp.VectorConverter)
+ override val bounce: Dp
+ get() = animatableBounce.value
+
+ suspend fun animateBounce() {
+ animatableBounce.animateTo(BounceSize)
+ animatableBounce.animateTo(0.dp)
+ }
+
+ private companion object {
+ val BounceSize = 8.dp
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index ee12736f6db4..be6ce5c5b4f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -43,13 +43,13 @@ data class UnloadedEditTileViewModel(
) {
fun load(context: Context): EditTileViewModel {
return EditTileViewModel(
- tileSpec,
- icon,
- label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
- appName?.toAnnotatedString(context),
- isCurrent,
- availableEditActions,
- category,
+ tileSpec = tileSpec,
+ icon = icon,
+ label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+ appName = appName?.toAnnotatedString(context),
+ isCurrent = isCurrent,
+ availableEditActions = availableEditActions,
+ category = category,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 757e37e9341f..d652d5bec27b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -123,7 +123,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
@@ -1859,16 +1858,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
private float getLockIconPadding() {
float lockIconPadding = 0f;
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.device_entry_icon_view);
- if (deviceEntryIconView != null) {
- lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - deviceEntryIconView.getTop();
- }
- } else if (mLockIconViewController.getTop() != 0f) {
+ View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.device_entry_icon_view);
+ if (deviceEntryIconView != null) {
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - mLockIconViewController.getTop();
+ - deviceEntryIconView.getTop();
}
return lockIconPadding;
}
@@ -5059,8 +5053,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return false;
}
- if (DeviceEntryUdfpsRefactor.isEnabled()
- && mAlternateBouncerInteractor.isVisibleState()) {
+ if (mAlternateBouncerInteractor.isVisibleState()) {
// never send touches to shade if the alternate bouncer is showing
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index b7a95e989317..0e30f2b4bb30 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import dagger.Lazy;
@@ -62,7 +62,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarStateController mStatusBarStateController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Lazy<NotificationShadeWindowViewController> mNotifShadeWindowViewController;
@@ -83,7 +83,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- StatusBarWindowController statusBarWindowController,
+ StatusBarWindowControllerStore statusBarWindowControllerStore,
DeviceProvisionedController deviceProvisionedController,
NotificationShadeWindowController notificationShadeWindowController,
@DisplayId int displayId,
@@ -102,7 +102,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mNpvc = shadeViewControllerLazy;
mStatusBarStateController = statusBarStateController;
- mStatusBarWindowController = statusBarWindowController;
+ mStatusBarWindowControllerStore = statusBarWindowControllerStore;
mDeviceProvisionedController = deviceProvisionedController;
mGutsManager = gutsManager;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -315,7 +315,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
// Update the visibility of notification shade and status bar window.
mNotificationShadeWindowController.setPanelVisible(false);
- mStatusBarWindowController.setForceStatusBarVisible(false);
+ mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(false);
// Close any guts that might be visible
mGutsManager.get().closeAndSaveGuts(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 3ad76b719470..7eff8124368b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.core
import android.app.Fragment
import androidx.annotation.VisibleForTesting
import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
@@ -27,9 +26,11 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import java.lang.IllegalStateException
-import javax.inject.Inject
import javax.inject.Provider
/**
@@ -65,13 +66,17 @@ interface StatusBarInitializer {
statusBarTransitions: PhoneStatusBarTransitions,
)
}
+
+ interface Factory {
+ fun create(displayId: Int): StatusBarInitializer
+ }
}
-@SysUISingleton
class StatusBarInitializerImpl
-@Inject
+@AssistedInject
constructor(
- private val windowController: StatusBarWindowController,
+ @Assisted private val displayId: Int,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
) : CoreStartable, StatusBarInitializer {
@@ -106,7 +111,7 @@ constructor(
private fun doStart() {
initialized = true
- windowController.fragmentHostManager
+ statusBarWindowControllerStore.defaultDisplay.fragmentHostManager
.addTagListener(
CollapsedStatusBarFragment.TAG,
object : FragmentHostManager.FragmentListener {
@@ -137,4 +142,9 @@ constructor(
)
.commit()
}
+
+ @AssistedFactory
+ interface Factory : StatusBarInitializer.Factory {
+ override fun create(displayId: Int): StatusBarInitializerImpl
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
new file mode 100644
index 000000000000..8d044bb9ce87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Provides per display instances of [StatusBarInitializer]. */
+interface StatusBarInitializerStore {
+ /**
+ * The instance for the default/main display of the device. For example, on a phone or a tablet,
+ * the default display is the internal/built-in display of the device.
+ *
+ * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
+ */
+ val defaultDisplay: StatusBarInitializer
+
+ /**
+ * Returns an instance for a specific display id.
+ *
+ * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
+ * displays.
+ */
+ fun forDisplay(displayId: Int): StatusBarInitializer
+}
+
+@SysUISingleton
+class MultiDisplayStatusBarInitializerStore
+@Inject
+constructor(
+ @Background private val backgroundApplicationScope: CoroutineScope,
+ private val factory: StatusBarInitializer.Factory,
+ private val displayRepository: DisplayRepository,
+) : StatusBarInitializerStore, CoreStartable {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ private val perDisplayInitializers = ConcurrentHashMap<Int, StatusBarInitializer>()
+
+ override val defaultDisplay: StatusBarInitializer
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): StatusBarInitializer {
+ if (displayRepository.getDisplay(displayId) == null) {
+ throw IllegalArgumentException("Display with id $displayId doesn't exist.")
+ }
+ return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) }
+ }
+
+ override fun start() {
+ backgroundApplicationScope.launch(
+ CoroutineName("MultiDisplayStatusBarInitializerStore#start")
+ ) {
+ displayRepository.displayRemovalEvent.collect { removedDisplayId ->
+ perDisplayInitializers.remove(removedDisplayId)
+ }
+ }
+ }
+}
+
+@SysUISingleton
+class SingleDisplayStatusBarInitializerStore
+@Inject
+constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore {
+
+ init {
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ }
+
+ private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY)
+
+ override val defaultDisplay: StatusBarInitializer = defaultInstance
+
+ override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index 8bd990b83a63..d372eb29b27b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -36,7 +36,7 @@ import com.android.systemui.statusbar.phone.AutoHideController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import com.android.wm.shell.bubbles.Bubbles
@@ -63,8 +63,8 @@ class StatusBarOrchestrator
constructor(
@Application private val applicationScope: CoroutineScope,
private val statusBarInitializer: StatusBarInitializer,
- private val statusBarWindowController: StatusBarWindowController,
private val statusBarModeRepository: StatusBarModeRepositoryStore,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val demoModeController: DemoModeController,
private val pluginDependencyProvider: PluginDependencyProvider,
private val autoHideController: AutoHideController,
@@ -157,7 +157,7 @@ constructor(
private fun createAndAddWindow() {
initializeStatusBarFragment()
- statusBarWindowController.attach()
+ statusBarWindowControllerStore.defaultDisplay.attach()
}
private fun initializeStatusBarFragment() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index cd1642eee4b4..5aad11fe1034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -77,16 +77,6 @@ abstract class StatusBarModule {
@Provides
@SysUISingleton
- fun defaultStatusBarWindowController(
- context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
- factory: StatusBarWindowControllerImpl.Factory,
- ): StatusBarWindowController {
- return factory.create(context, viewCaptureAwareWindowManager)
- }
-
- @Provides
- @SysUISingleton
fun windowControllerStore(
multiDisplayImplLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>,
singleDisplayImplLazy: Lazy<SingleDisplayStatusBarWindowControllerStore>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 118f5f0515be..bf7e879bb72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -34,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -44,8 +44,8 @@ import kotlin.math.roundToInt
*/
class SystemEventChipAnimationController @Inject constructor(
private val context: Context,
- private val statusBarWindowController: StatusBarWindowController,
- private val contentInsetsProvider: StatusBarContentInsetsProvider
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val contentInsetsProvider: StatusBarContentInsetsProvider,
) : SystemStatusAnimationCallback {
private lateinit var animationWindowView: FrameLayout
@@ -244,7 +244,7 @@ class SystemEventChipAnimationController @Inject constructor(
val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height)
val lp = FrameLayout.LayoutParams(MATCH_PARENT, height)
lp.gravity = Gravity.END or Gravity.TOP
- statusBarWindowController.addViewToWindow(animationWindowView, lp)
+ statusBarWindowControllerStore.defaultDisplay.addViewToWindow(animationWindowView, lp)
animationWindowView.clipToPadding = false
animationWindowView.clipChildren = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index f0e60dd2ce54..e34f61df8e88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -23,7 +23,7 @@ import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.AnimatorSet
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.Assert
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
@@ -65,7 +65,7 @@ open class SystemStatusAnimationSchedulerImpl
constructor(
private val coordinator: SystemEventCoordinator,
private val chipAnimationController: SystemEventChipAnimationController,
- private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
dumpManager: DumpManager,
private val systemClock: SystemClock,
@Application private val coroutineScope: CoroutineScope,
@@ -277,7 +277,7 @@ constructor(
private fun runChipAppearAnimation() {
Assert.isMainThread()
if (hasPersistentDot) {
- statusBarWindowController.setForceStatusBarVisible(true)
+ statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(true)
}
animationState.value = ANIMATING_IN
@@ -311,7 +311,7 @@ constructor(
scheduledEvent.value != null -> ANIMATION_QUEUED
else -> IDLE
}
- statusBarWindowController.setForceStatusBarVisible(false)
+ statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(false)
}
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 693ae6617feb..ebaa3c436de4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -20,7 +20,7 @@ import android.content.Context
import android.view.MotionEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import javax.inject.Inject
/** A class to detect when a user swipes away the status bar. */
@@ -31,10 +31,11 @@ constructor(
context: Context,
displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
- private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
// Gesture starts just below the status bar
+ val statusBarWindowController = statusBarWindowControllerStore.defaultDisplay
return ev.y >= statusBarWindowController.statusBarHeight &&
ev.y <= 3 * statusBarWindowController.statusBarHeight
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index b90aa107d617..71f9c07ba2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row
import android.widget.flags.Flags.notifLinearlayoutOptimized
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
import javax.inject.Inject
import javax.inject.Provider
@@ -35,6 +36,7 @@ constructor(
bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory,
notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>,
+ notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory,
) : NotifRemoteViewsFactoryContainer {
override val factories: Set<NotifRemoteViewsFactory> = buildSet {
add(precomputedTextViewFactory)
@@ -47,5 +49,8 @@ constructor(
if (NotificationViewFlipperPausing.isEnabled) {
add(notificationViewFlipperFactory.get())
}
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ add(notificationRowIconViewInflaterFactory)
+ }
}
}
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 e10fd8f3b3fb..41abac1d47f7 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
@@ -342,7 +342,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
* Cancel any pending content view frees from {@link #freeNotificationView} for the provided
* content views.
*
- * @param row top level notification row containing the content views
+ * @param row top level notification row containing the content views
* @param contentViews content views to cancel pending frees on
*/
private void cancelContentViewFrees(
@@ -478,6 +478,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
setRemoteViewsInflaterFactory(result.newPublicView,
notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ setRemoteViewsInflaterFactory(result.mNewGroupHeaderView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER));
+ setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView,
+ notifLayoutInflaterFactoryProvider.provide(row,
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER));
+ }
}
private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -516,6 +523,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
logger.logAsyncTaskProgress(entry, "contracted view applied");
result.inflatedContentView = v;
}
+
@Override
public RemoteViews getRemoteView() {
return result.newContentView;
@@ -1406,6 +1414,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
abstract static class ApplyCallback {
public abstract void setResultView(View v);
+
public abstract RemoteViews getRemoteView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 84f2f6670839..43ade5cc5a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import dagger.Binds;
@@ -28,7 +29,7 @@ import javax.inject.Provider;
/**
* Dagger Module containing notification row and view inflation implementations.
*/
-@Module
+@Module(includes = {AppIconProviderModule.class})
public abstract class NotificationRowModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
new file mode 100644
index 000000000000..24b5cf1aa33b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.app.ActivityManager
+import android.app.Flags
+import android.content.Context
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import com.android.internal.R
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/** A provider used to cache and fetch app icons used by notifications. */
+interface AppIconProvider {
+ @Throws(NameNotFoundException::class)
+ fun getOrFetchAppIcon(packageName: String, context: Context): Drawable
+}
+
+@SysUISingleton
+class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider {
+ private val iconFactory: BaseIconFactory
+ get() {
+ val isLowRam = ActivityManager.isLowRamDeviceStatic()
+ val res = sysuiContext.resources
+ val iconSize: Int =
+ res.getDimensionPixelSize(
+ if (isLowRam) R.dimen.notification_small_icon_size_low_ram
+ else R.dimen.notification_small_icon_size
+ )
+ return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize)
+ }
+
+ override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+ val icon = context.packageManager.getApplicationIcon(packageName)
+ return BitmapDrawable(
+ context.resources,
+ iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE),
+ )
+ }
+}
+
+class NoOpIconProvider : AppIconProvider {
+ companion object {
+ const val TAG = "NoOpIconProvider"
+ }
+
+ override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+ Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.")
+ return ColorDrawable(Color.WHITE)
+ }
+}
+
+@Module
+class AppIconProviderModule {
+ @Provides
+ @SysUISingleton
+ fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider =
+ if (Flags.notificationsRedesignAppIcons()) {
+ realImpl.get()
+ } else {
+ NoOpIconProvider()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
new file mode 100644
index 000000000000..2522e58a08a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.NotificationRowIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
+import javax.inject.Inject
+
+/**
+ * A factory which owns the construction of any NotificationRowIconView inside of Notifications in
+ * SystemUI. This allows overriding the small icon with the app icon in notifications.
+ */
+class NotificationRowIconViewInflaterFactory
+@Inject
+constructor(private val appIconProvider: AppIconProvider) : NotifRemoteViewsFactory {
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @NotificationRowContentBinder.InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet,
+ ): View? {
+ return when (name) {
+ NotificationRowIconView::class.java.name ->
+ NotificationRowIconView(context, attrs).also { view ->
+ val sbn = row.entry.sbn
+ view.setIconProvider(
+ object : NotificationRowIconView.NotificationIconProvider {
+ override fun shouldShowAppIcon(): Boolean {
+ // TODO(b/371174789): implement me
+ return true
+ }
+
+ override fun getAppIcon(): Drawable {
+ return appIconProvider.getOrFetchAppIcon(sbn.packageName, context)
+ }
+ }
+ )
+ }
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 5b37468c9da6..d1338eadb6b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -58,7 +58,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.kotlin.getOrNull
@@ -93,7 +93,7 @@ constructor(
@Main private val mainExecutor: DelayableExecutor,
private val shadeControllerLazy: Lazy<ShadeController>,
private val communalSceneInteractor: CommunalSceneInteractor,
- private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
@@ -562,7 +562,7 @@ constructor(
}
val rootView = animationController.transitionContainer.rootView
val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
- statusBarWindowController.wrapAnimationControllerIfInStatusBar(
+ statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
rootView,
animationController
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7e5b45543e9e..3d7cd9c9fbcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -128,7 +128,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
import com.android.systemui.flags.FeatureFlags;
@@ -226,7 +225,7 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
import com.android.systemui.util.DumpUtilsKt;
@@ -372,7 +371,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarInitializer mStatusBarInitializer;
- private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
private final StatusBarModeRepositoryStore mStatusBarModeRepository;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
@@ -610,7 +609,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
LightBarController lightBarController,
AutoHideController autoHideController,
StatusBarInitializer statusBarInitializer,
- StatusBarWindowController statusBarWindowController,
+ StatusBarWindowControllerStore statusBarWindowControllerStore,
StatusBarWindowStateController statusBarWindowStateController,
StatusBarModeRepositoryStore statusBarModeRepository,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -716,7 +715,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mStatusBarInitializer = statusBarInitializer;
- mStatusBarWindowController = statusBarWindowController;
+ mStatusBarWindowControllerStore = statusBarWindowControllerStore;
mStatusBarModeRepository = statusBarModeRepository;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -1894,7 +1893,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// When the StatusBarSimpleFragment flag is enabled, this logic will be done in
// StatusBarOrchestrator
if (!StatusBarSimpleFragment.isEnabled()) {
- mStatusBarWindowController.attach();
+ mStatusBarWindowControllerStore.getDefaultDisplay().attach();
}
}
@@ -2825,23 +2824,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
- && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f)) {
- // Assume scrim state for shade is already correct and do nothing
- } else {
- // Safeguard which prevents the scrim from being stuck in the wrong state
- mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
- }
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+ && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f)) {
+ // Assume scrim state for shade is already correct and do nothing
} else {
- if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
- && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f)) {
- mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
- } else {
- mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED);
- }
+ // Safeguard which prevents the scrim from being stuck in the wrong state
+ mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
}
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
@@ -3168,12 +3157,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
public void onDozeAmountChanged(float linear, float eased) {
if (!lightRevealMigration()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- // If wakeAndUnlocking, this is handled in AuthRippleInteractor
- if (!mBiometricUnlockController.isWakeAndUnlock()) {
- mLightRevealScrim.setRevealAmount(1f - linear);
- }
- } else {
+ // If wakeAndUnlocking, this is handled in AuthRippleInteractor
+ if (!mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 460423378dff..1cca3ae0a2c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -57,7 +57,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.kotlin.getOrNull
import dagger.Lazy
@@ -85,7 +85,7 @@ constructor(
private val context: Context,
@DisplayId private val displayId: Int,
private val lockScreenUserManager: NotificationLockscreenUserManager,
- private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -525,7 +525,7 @@ constructor(
}
val rootView = animationController.transitionContainer.rootView
val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
- statusBarWindowController.wrapAnimationControllerIfInStatusBar(
+ statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
rootView,
animationController
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d6716a0c4701..e7d9717defa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -40,7 +40,7 @@ import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.leak.RotationUtils;
@@ -49,7 +49,7 @@ import java.util.Objects;
public class PhoneStatusBarView extends FrameLayout {
private static final String TAG = "PhoneStatusBarView";
- private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
private int mRotationOrientation = -1;
@Nullable
@@ -75,7 +75,7 @@ public class PhoneStatusBarView extends FrameLayout {
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+ mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
}
void setTouchEventHandler(Gefingerpoken handler) {
@@ -326,7 +326,7 @@ public class PhoneStatusBarView extends FrameLayout {
if (Flags.statusBarStopUpdatingWindowHeight()) {
return;
}
- mStatusBarWindowController.refreshStatusBarHeight();
+ mStatusBarWindowControllerStore.getDefaultDisplay().refreshStatusBarHeight();
}
interface HasCornerCutoutFetcher {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 8f2d4f931b91..7145ffe1f515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import javax.inject.Inject;
@@ -38,7 +38,7 @@ import javax.inject.Inject;
@SysUISingleton
public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable {
private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
private final ShadeViewController mShadeViewController;
private final PanelExpansionInteractor mPanelExpansionInteractor;
private final NotificationStackScrollLayoutController mNsslController;
@@ -50,7 +50,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener,
@Inject
StatusBarHeadsUpChangeListener(
NotificationShadeWindowController notificationShadeWindowController,
- StatusBarWindowController statusBarWindowController,
+ StatusBarWindowControllerStore statusBarWindowControllerStore,
ShadeViewController shadeViewController,
PanelExpansionInteractor panelExpansionInteractor,
NotificationStackScrollLayoutController nsslController,
@@ -59,7 +59,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener,
StatusBarStateController statusBarStateController,
NotificationRemoteInputManager notificationRemoteInputManager) {
mNotificationShadeWindowController = notificationShadeWindowController;
- mStatusBarWindowController = statusBarWindowController;
+ mStatusBarWindowControllerStore = statusBarWindowControllerStore;
mShadeViewController = shadeViewController;
mPanelExpansionInteractor = panelExpansionInteractor;
mNsslController = nsslController;
@@ -78,7 +78,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener,
public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
if (inPinnedMode) {
mNotificationShadeWindowController.setHeadsUpShowing(true);
- mStatusBarWindowController.setForceStatusBarVisible(true);
+ mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(true);
if (mPanelExpansionInteractor.isFullyCollapsed()) {
mShadeViewController.updateTouchableRegion();
}
@@ -93,7 +93,9 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener,
// open artificially.
mNotificationShadeWindowController.setHeadsUpShowing(false);
if (bypassKeyguard) {
- mStatusBarWindowController.setForceStatusBarVisible(false);
+ mStatusBarWindowControllerStore
+ .getDefaultDisplay()
+ .setForceStatusBarVisible(false);
}
} else {
// we need to keep the panel open artificially, let's wait until the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 5b0319883b5f..5f864e5dc53a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -15,12 +15,18 @@
*/
package com.android.systemui.statusbar.phone.dagger
+import android.view.Display
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Default
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.core.CommandQueueInitializer
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.core.StatusBarInitializerImpl
+import com.android.systemui.statusbar.core.StatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarOrchestrator
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
@@ -47,15 +53,31 @@ interface StatusBarPhoneModule {
impl: CentralSurfacesCommandQueueCallbacks
): CommandQueue.Callbacks
+ @Binds
+ fun initializerFactory(
+ implFactory: StatusBarInitializerImpl.Factory
+ ): StatusBarInitializer.Factory
+
/** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
@Binds
@IntoMap
@ClassKey(StatusBarInitializerImpl::class)
- fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable
+ fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable
- @Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+ @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
companion object {
+ // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the
+ // Default qualifier for this reason.
+ @Default
+ @Provides
+ @SysUISingleton
+ fun statusBarInitializerImpl(
+ implFactory: StatusBarInitializerImpl.Factory
+ ): StatusBarInitializerImpl {
+ return implFactory.create(displayId = Display.DEFAULT_DISPLAY)
+ }
+
@Provides
@SysUISingleton
@IntoMap
@@ -83,5 +105,32 @@ interface StatusBarPhoneModule {
CoreStartable.NOP
}
}
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarInitializerStore::class)
+ fun initializerStoreAsCoreStartable(
+ multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayStoreLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ fun initializerStore(
+ singleDisplayStoreLazy: Lazy<SingleDisplayStatusBarInitializerStore>,
+ multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore>,
+ ): StatusBarInitializerStore {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayStoreLazy.get()
+ } else {
+ singleDisplayStoreLazy.get()
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index f026b99af49c..cf877a741d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import dagger.Module;
import dagger.Provides;
@@ -125,9 +126,9 @@ public interface StatusBarFragmentModule {
@StatusBarFragmentScope
static PhoneStatusBarTransitions providePhoneStatusBarTransitions(
@RootView PhoneStatusBarView view,
- StatusBarWindowController statusBarWindowController
- ) {
- return new PhoneStatusBarTransitions(view, statusBarWindowController.getBackgroundView());
+ StatusBarWindowControllerStore statusBarWindowControllerStore) {
+ return new PhoneStatusBarTransitions(
+ view, statusBarWindowControllerStore.getDefaultDisplay().getBackgroundView());
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index bd6a1c05ddc9..3cf8c3f48409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -52,7 +52,7 @@ import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.policy.CallbackController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.util.concurrent.Executor
@@ -75,7 +75,7 @@ constructor(
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
- private val statusBarWindowController: StatusBarWindowController,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
private val statusBarModeRepository: StatusBarModeRepositoryStore,
@OngoingCallLog private val logger: LogBuffer,
@@ -205,7 +205,9 @@ constructor(
this.chipView = chipView
val backgroundView: ChipBackgroundContainer? =
chipView.findViewById(R.id.ongoing_activity_chip_background)
- backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
+ backgroundView?.maxHeightFetcher = {
+ statusBarWindowControllerStore.defaultDisplay.statusBarHeight
+ }
if (hasOngoingCall()) {
updateChip()
}
@@ -339,7 +341,8 @@ constructor(
// But, this class still needs to do the non-display logic regardless of the flag.
uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
- statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
+ statusBarWindowControllerStore.defaultDisplay
+ .setOngoingProcessRequiresStatusBarVisible(true)
}
updateGestureListening()
sendStateChangeEvent()
@@ -405,7 +408,9 @@ constructor(
if (!Flags.statusBarScreenSharingChips()) {
tearDownChipView()
}
- statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+ false
+ )
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
sendStateChangeEvent()
uidObserver.unregister()
@@ -429,7 +434,9 @@ constructor(
private fun onSwipeAwayGestureDetected() {
logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" })
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
- statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+ statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+ false
+ )
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 5f30b3719aa7..7d0dadcf8c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.window
+import android.content.Context
import android.view.Display
import android.view.WindowManager
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
@@ -105,12 +106,19 @@ constructor(
@SysUISingleton
class SingleDisplayStatusBarWindowControllerStore
@Inject
-constructor(private val controller: StatusBarWindowController) : StatusBarWindowControllerStore {
+constructor(
+ context: Context,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ factory: StatusBarWindowControllerImpl.Factory,
+) : StatusBarWindowControllerStore {
init {
StatusBarConnectedDisplays.assertInLegacyMode()
}
+ private val controller: StatusBarWindowController =
+ factory.create(context, viewCaptureAwareWindowManager)
+
override val defaultDisplay = controller
override fun forDisplay(displayId: Int) = controller
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index ae635b8cbfd4..df50f765349c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -77,6 +77,7 @@ import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.RingerModeLiveData;
@@ -125,6 +126,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private LightBarController mLightBarController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore;
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@Mock private UiEventLogger mUiEventLogger;
@@ -155,7 +157,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
when(mResources.getConfiguration()).thenReturn(
getContext().getResources().getConfiguration());
-
+ when(mStatusBarWindowControllerStore.getDefaultDisplay())
+ .thenReturn(mStatusBarWindowController);
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
mInteractor = mKosmos.getGlobalActionsInteractor();
@@ -184,7 +187,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mStatusBarService,
mLightBarController,
mNotificationShadeWindowController,
- mStatusBarWindowController,
+ mStatusBarWindowControllerStore,
mWindowManager,
mBackgroundExecutor,
mUiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 96a0aadacbc3..ecc62e908a4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -298,7 +298,7 @@ class ClockSectionTest : SysuiTestCase() {
underTest.applyDefaultConstraints(cs)
val referencedIds =
cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom)
- referencedIds.contentEquals(intArrayOf(R.id.lockscreen_clock_view))
+ referencedIds.contentEquals(intArrayOf(customR.id.lockscreen_clock_view))
}
@Test
@@ -323,7 +323,7 @@ class ClockSectionTest : SysuiTestCase() {
}
private fun assertLargeClockTop(cs: ConstraintSet, expectedLargeClockTopMargin: Int) {
- val largeClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
+ val largeClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view_large)
assertThat(largeClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
assertThat(largeClockConstraint.layout.topMargin).isEqualTo(expectedLargeClockTopMargin)
}
@@ -332,7 +332,7 @@ class ClockSectionTest : SysuiTestCase() {
val smallClockGuidelineConstraint = cs.getConstraint(R.id.small_clock_guideline_top)
assertThat(smallClockGuidelineConstraint.layout.topToTop).isEqualTo(-1)
- val smallClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view)
+ val smallClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view)
assertThat(smallClockConstraint.layout.topToBottom)
.isEqualTo(R.id.small_clock_guideline_top)
assertThat(smallClockConstraint.layout.topMargin).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index bfb8a57e6271..cea51a89a378 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -18,13 +18,11 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.graphics.Point
-import android.platform.test.annotations.DisableFlags
import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.LegacyLockIconViewController
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -60,7 +58,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
@Mock private lateinit var notificationPanelView: NotificationPanelView
private lateinit var featureFlags: FakeFeatureFlags
- @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var deviceEntryIconViewModel: DeviceEntryIconViewModel
private lateinit var underTest: DefaultDeviceEntrySection
@@ -81,7 +78,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
context,
notificationPanelView,
featureFlags,
- { lockIconViewController },
{ deviceEntryIconViewModel },
{ mock(DeviceEntryForegroundViewModel::class.java) },
{ mock(DeviceEntryBackgroundViewModel::class.java) },
@@ -102,37 +98,13 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
@Test
fun addViewsConditionally_migrateAndRefactorFlagsOn() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun addViewsConditionally_migrateFlagOff() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- val constraintLayout = ConstraintLayout(context, null)
- underTest.addViews(constraintLayout)
- assertThat(constraintLayout.childCount).isEqualTo(0)
- }
-
- @Test
- fun applyConstraints_udfps_refactor_off() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- val cs = ConstraintSet()
- underTest.applyConstraints(cs)
-
- val constraint = cs.getConstraint(R.id.lock_icon_view)
-
- assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
- fun applyConstraints_udfps_refactor_on() {
- mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ fun applyConstraints() {
whenever(deviceEntryIconViewModel.isUdfpsSupported).thenReturn(MutableStateFlow(false))
val cs = ConstraintSet()
underTest.applyConstraints(cs)
@@ -144,24 +116,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
}
@Test
- fun testCenterIcon_udfps_refactor_off() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- val cs = ConstraintSet()
- underTest.centerIcon(Point(5, 6), 1F, cs)
-
- val constraint = cs.getConstraint(R.id.lock_icon_view)
-
- assertThat(constraint.layout.mWidth).isEqualTo(2)
- assertThat(constraint.layout.mHeight).isEqualTo(2)
- assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.topMargin).isEqualTo(5)
- assertThat(constraint.layout.startMargin).isEqualTo(4)
- }
-
- @Test
- fun testCenterIcon_udfps_refactor_on() {
- mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ fun testCenterIcon() {
val cs = ConstraintSet()
underTest.centerIcon(Point(5, 6), 1F, cs)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt
new file mode 100644
index 000000000000..0d1d37af7e5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarInitializerStoreTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().also {
+ // Using unconfinedTestDispatcher to avoid having to call `runCurrent` in the tests.
+ it.testDispatcher = it.unconfinedTestDispatcher
+ }
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+ private val store = kosmos.multiDisplayStatusBarInitializerStore
+
+ @Before
+ fun start() {
+ store.start()
+ }
+
+ @Before
+ fun addDisplays() = runBlocking {
+ fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY_ID)
+ fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID)
+ }
+
+ @Test
+ fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() =
+ testScope.runTest {
+ val controller = store.defaultDisplay
+
+ assertThat(store.defaultDisplay).isSameInstanceAs(controller)
+ }
+
+ @Test
+ fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() =
+ testScope.runTest {
+ val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller)
+ }
+
+ @Test
+ fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() =
+ testScope.runTest {
+ val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
+ fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun forDisplay_nonExistingDisplayId_throws() =
+ testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }
+
+ companion object {
+ private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
+ private const val NON_DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1
+ private const val NON_EXISTING_DISPLAY_ID = Display.DEFAULT_DISPLAY + 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 376873d19624..5d8a8fd03bc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.statusbar.BatteryStatusChip
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -63,6 +64,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
@Mock private lateinit var systemEventCoordinator: SystemEventCoordinator
@Mock private lateinit var statusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
@Mock private lateinit var statusBarContentInsetProvider: StatusBarContentInsetsProvider
@@ -82,12 +84,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
+ whenever(statusBarWindowControllerStore.defaultDisplay)
+ .thenReturn(statusBarWindowController)
systemClock = FakeSystemClock()
chipAnimationController =
SystemEventChipAnimationController(
mContext,
- statusBarWindowController,
- statusBarContentInsetProvider
+ statusBarWindowControllerStore,
+ statusBarContentInsetProvider,
)
// StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
@@ -660,7 +664,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
SystemStatusAnimationSchedulerImpl(
systemEventCoordinator,
chipAnimationController,
- statusBarWindowController,
+ statusBarWindowControllerStore,
dumpManager,
systemClock,
CoroutineScope(StandardTestDispatcher(testScope.testScheduler)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 15ea811287b8..44d81a7abfe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
-import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -170,6 +169,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
import com.android.systemui.statusbar.core.StatusBarOrchestrator;
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -194,6 +194,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.WallpaperController;
@@ -292,6 +293,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private AutoHideController mAutoHideController;
@Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore;
@Mock private Provider<CollapsedStatusBarFragment> mCollapsedStatusBarFragmentProvider;
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private Bubbles mBubbles;
@@ -383,6 +385,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
when(mBubbles.canShowBubbleNotification()).thenReturn(true);
+ when(mStatusBarWindowControllerStore.getDefaultDisplay())
+ .thenReturn(mStatusBarWindowController);
+
mVisualInterruptionDecisionProvider =
VisualInterruptionDecisionProviderTestUtil.INSTANCE.createProviderByFlag(
mAmbientDisplayConfiguration,
@@ -465,7 +470,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mKeyguardStateController,
mStatusBarStateController,
mStatusBarKeyguardViewManager,
- mStatusBarWindowController,
+ mStatusBarWindowControllerStore,
mDeviceProvisionedController,
mNotificationShadeWindowController,
0,
@@ -507,10 +512,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mLightBarController,
mAutoHideController,
new StatusBarInitializerImpl(
- mStatusBarWindowController,
+ mContext.getDisplayId(),
+ mStatusBarWindowControllerStore,
mCollapsedStatusBarFragmentProvider,
emptySet()),
- mStatusBarWindowController,
+ mStatusBarWindowControllerStore,
mStatusBarWindowStateController,
new FakeStatusBarModeRepository(),
mKeyguardUpdateMonitor,
@@ -852,34 +858,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
- verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
- }
-
- @Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN device occluded and panel is NOT expanded
- mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED));
- }
-
- @Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
@@ -895,7 +873,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
@@ -911,21 +888,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN device occluded and qs IS expanded
- mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
- }
-
- @Test
public void testEnteringGlanceableHub_updatesScrim() {
// Transition to the glanceable hub.
mKosmos.getCommunalRepository()
@@ -1149,6 +1111,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(StatusBarSimpleFragment.FLAG_NAME)
public void bubbleBarVisibility() {
createCentralSurfaces();
mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 68df7488ee10..ee79ca0df9d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -57,10 +58,15 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
get() = view.requireViewById(R.id.system_icons)
private val windowController = mock<StatusBarWindowController>()
+ private val windowControllerStore = mock<StatusBarWindowControllerStore>()
@Before
fun setUp() {
- mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
+ whenever(windowControllerStore.defaultDisplay).thenReturn(windowController)
+ mDependency.injectTestDependency(
+ StatusBarWindowControllerStore::class.java,
+ windowControllerStore,
+ )
context.ensureTestableResources()
view = spy(createStatusBarView())
whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9cfb0bb3900b..1ceb20adeebd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -46,6 +46,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
@@ -197,6 +198,8 @@ import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.Transitions;
+import kotlin.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -215,7 +218,6 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
-import kotlin.Lazy;
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -2470,6 +2472,52 @@ public class BubblesTest extends SysuiTestCase {
verify(stackView, never()).showOverflow(anyBoolean());
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_addBubble() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED));
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_updateBubble() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ // Mark the notification as updated
+ NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build();
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED));
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragBubbleToDismiss() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
@@ -2655,6 +2703,10 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
}
+ private Bubble eqBubbleWithKey(String key) {
+ return argThat(b -> b.getKey().equals(key));
+ }
+
private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener {
int mStateChangeCalls = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS
new file mode 100644
index 000000000000..eae8629231f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS
@@ -0,0 +1,5 @@
+# Bubbles team
+madym@google.com
+atsjenk@google.com
+liranb@google.com
+mpodolian@google.com \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index fcc83b3e579f..78ea70086605 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -53,6 +53,10 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
+ suspend fun addDisplay(displayId: Int, type: Int = Display.TYPE_EXTERNAL) {
+ addDisplay(display(type, id = displayId))
+ }
+
suspend fun addDisplay(display: Display) {
flow.value += display
displayAdditionEventFlow.emit(display)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index ddcc6d60f993..b9f0c9a70d3d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -37,7 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.deviceProvisionedController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -67,7 +67,7 @@ val Kosmos.shadeControllerImpl by
mock<KeyguardStateController>(),
statusBarStateController,
statusBarKeyguardViewManager,
- mock<StatusBarWindowController>(),
+ mock<StatusBarWindowControllerStore>(),
deviceProvisionedController,
mock<NotificationShadeWindowController>(),
0,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
new file mode 100644
index 000000000000..73ed228f5aaa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+
+class FakeStatusBarInitializerFactory(
+ private val statusBarViewController: PhoneStatusBarViewController,
+ private val statusBarTransitions: PhoneStatusBarTransitions,
+) : StatusBarInitializer.Factory {
+
+ override fun create(displayId: Int): StatusBarInitializer =
+ FakeStatusBarInitializer(statusBarViewController, statusBarTransitions)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index d10320004454..7ad715ba5a89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.core
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
import com.android.systemui.statusbar.phone.phoneStatusBarViewController
@@ -26,3 +28,20 @@ val Kosmos.fakeStatusBarInitializer by
}
var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
+
+val Kosmos.fakeStatusBarInitializerFactory by
+ Kosmos.Fixture {
+ FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions)
+ }
+
+var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by
+ Kosmos.Fixture { fakeStatusBarInitializerFactory }
+
+val Kosmos.multiDisplayStatusBarInitializerStore by
+ Kosmos.Fixture {
+ MultiDisplayStatusBarInitializerStore(
+ applicationCoroutineScope,
+ fakeStatusBarInitializerFactory,
+ displayRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index c53e44d514f7..54de293b8911 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepositor
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
import com.android.systemui.statusbar.phone.mockAutoHideController
import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
import com.android.wm.shell.bubbles.bubblesOptional
val Kosmos.statusBarOrchestrator by
@@ -36,8 +36,8 @@ val Kosmos.statusBarOrchestrator by
StatusBarOrchestrator(
applicationCoroutineScope,
fakeStatusBarInitializer,
- fakeStatusBarWindowController,
fakeStatusBarModeRepository,
+ fakeStatusBarWindowControllerStore,
mockDemoModeController,
mockPluginDependencyProvider,
mockAutoHideController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index fc4f05df26ed..a7a61957d104 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -69,6 +69,8 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -99,7 +101,7 @@ import org.mockito.Mockito
class ExpandableNotificationRowBuilder(
private val context: Context,
dependency: TestableDependency,
- private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
+ private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
) {
private val mMockLogger: ExpandableNotificationRowLogger
@@ -161,21 +163,21 @@ class ExpandableNotificationRowBuilder(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
"true",
- true
+ true,
)
setProperty(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_ENABLED,
"true",
- true
+ true,
)
setProperty(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
"false",
- true
+ true,
)
- }
+ },
)
val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags)
val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY)
@@ -192,21 +194,21 @@ class ExpandableNotificationRowBuilder(
Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY),
remoteInputManager = remoteInputManager,
smartReplyController = mSmartReplyController,
- context = context
+ context = context,
),
smartActionsInflater =
SmartActionInflaterImpl(
constants = mSmartReplyConstants,
activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY),
smartReplyController = mSmartReplyController,
- headsUpManager = mHeadsUpManager
- )
+ headsUpManager = mHeadsUpManager,
+ ),
)
val notifLayoutInflaterFactoryProvider =
object : NotifLayoutInflaterFactory.Provider {
override fun provide(
row: ExpandableNotificationRow,
- layoutType: Int
+ layoutType: Int,
): NotifLayoutInflaterFactory =
NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories)
}
@@ -270,14 +272,14 @@ class ExpandableNotificationRowBuilder(
whenever(
mOnUserInteractionCallback.registerFutureDismissal(
ArgumentMatchers.any(),
- ArgumentMatchers.anyInt()
+ ArgumentMatchers.anyInt(),
)
)
.thenReturn(mFutureDismissalRunnable)
}
private fun getNotifRemoteViewsFactoryContainer(
- featureFlags: FeatureFlags,
+ featureFlags: FeatureFlags
): NotifRemoteViewsFactoryContainer {
return NotifRemoteViewsFactoryContainerImpl(
featureFlags,
@@ -285,6 +287,7 @@ class ExpandableNotificationRowBuilder(
BigPictureLayoutInflaterFactory(),
NotificationOptimizedLinearLayoutFactory(),
{ Mockito.mock(NotificationViewFlipperFactory::class.java) },
+ NotificationRowIconViewInflaterFactory(AppIconProviderImpl(context)),
)
}
@@ -293,7 +296,7 @@ class ExpandableNotificationRowBuilder(
NotificationChannel(
notification.channelId,
notification.channelId,
- NotificationManager.IMPORTANCE_DEFAULT
+ NotificationManager.IMPORTANCE_DEFAULT,
)
channel.isBlockable = true
val entry =
@@ -321,7 +324,7 @@ class ExpandableNotificationRowBuilder(
private fun generateRow(
entry: NotificationEntry,
- @InflationFlag extraInflationFlags: Int
+ @InflationFlag extraInflationFlags: Int,
): ExpandableNotificationRow {
// NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
// set, but we do not want to override an existing value that is needed by a specific test.
@@ -329,7 +332,7 @@ class ExpandableNotificationRowBuilder(
val rowInflaterTask =
RowInflaterTask(
mFakeSystemClock,
- Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY)
+ Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY),
)
val row = rowInflaterTask.inflateSynchronously(context, null, entry)
@@ -364,7 +367,7 @@ class ExpandableNotificationRowBuilder(
mSmartReplyController,
featureFlags,
Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
- Mockito.mock(UiEventLogger::class.java, STUB_ONLY)
+ Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
)
row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
new file mode 100644
index 000000000000..08c6bbab6dd6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt
index d19e3227027c..35f95b6fab8f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt
@@ -22,10 +22,10 @@ class FakeStatusBarWindowControllerStore : StatusBarWindowControllerStore {
private val perDisplayControllers = mutableMapOf<Int, FakeStatusBarWindowController>()
- override val defaultDisplay
+ override val defaultDisplay: FakeStatusBarWindowController
get() = forDisplay(Display.DEFAULT_DISPLAY)
- override fun forDisplay(displayId: Int): StatusBarWindowController {
+ override fun forDisplay(displayId: Int): FakeStatusBarWindowController {
return perDisplayControllers.computeIfAbsent(displayId) { FakeStatusBarWindowController() }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index 6c6f243f3953..78caf93d4618 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos
val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() }
-var Kosmos.statusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController }
+var Kosmos.statusBarWindowController: StatusBarWindowController by
+ Kosmos.Fixture { fakeStatusBarWindowController }
val Kosmos.fakeStatusBarWindowControllerStore by
Kosmos.Fixture { FakeStatusBarWindowControllerStore() }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 8896d772ea4d..bfa801f30955 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -8,7 +8,7 @@ package {
// OWNER: g/ravenwood
// Bug component: 25698
- default_team: "trendy_team_framework_backstage_power",
+ default_team: "trendy_team_ravenwood",
}
filegroup {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 908e5903122e..5894476b9201 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -335,7 +335,11 @@ public class RavenwoodRuntimeEnvironmentController {
}
android.os.Process.reset$ravenwood();
- ResourcesManager.setInstance(null); // Better structure needed.
+ try {
+ ResourcesManager.setInstance(null); // Better structure needed.
+ } catch (Exception e) {
+ // AOSP-CHANGE: AOSP doesn't support resources yet.
+ }
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(true);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index 57c643bb08a1..a7aab49cde56 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -160,11 +160,13 @@ public class ContentSuggestionsManagerService extends
HardwareBuffer snapshotBuffer = null;
int colorSpaceId = 0;
+ TaskSnapshot snapshot = null;
// Skip taking TaskSnapshot when bitmap is provided.
if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
// Can block, so call before acquiring the lock.
- TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+ snapshot = mActivityTaskManagerInternal.getTaskSnapshotBlocking(
+ taskId, false /* isLowResolution */,
+ TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
if (snapshot != null) {
snapshotBuffer = snapshot.getHardwareBuffer();
ColorSpace colorSpace = snapshot.getColorSpace();
@@ -185,6 +187,9 @@ public class ContentSuggestionsManagerService extends
}
}
}
+ if (snapshot != null) {
+ snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 05d07ae761c1..485bf31dfb64 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1523,7 +1523,7 @@ public class BinaryTransparencyService extends SystemService {
@Override
public void onApexStaged(ApexStagedEvent event) throws RemoteException {
Slog.d(TAG, "A new APEX has been staged for update. There are currently "
- + event.stagedApexModuleNames.length + " APEX(s) staged for update. "
+ + event.stagedApexInfos.length + " APEX(s) staged for update. "
+ "Scheduling measurement...");
UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
BinaryTransparencyService.this);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f8857d3e152a..d13dd2f2e1fc 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -443,6 +443,11 @@ final class UiModeManagerService extends SystemService {
mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression;
}
+ @VisibleForTesting
+ void setCurrentUser(int currentUserId) {
+ mCurrentUser = currentUserId;
+ }
+
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
mCurrentUser = to.getUserIdentifier();
@@ -864,9 +869,9 @@ final class UiModeManagerService extends SystemService {
throw new IllegalArgumentException("Unknown mode: " + mode);
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -929,7 +934,7 @@ final class UiModeManagerService extends SystemService {
@AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
setAttentionModeThemeOverlay_enforcePermission();
- enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
@@ -1020,16 +1025,16 @@ final class UiModeManagerService extends SystemService {
return false;
}
final int user = Binder.getCallingUserHandle().getIdentifier();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
-
if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) {
Slog.e(TAG, "Target user is not current user,"
+ " INTERACT_ACROSS_USERS permission is required");
return false;
-
}
+
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
// Store the last requested bedtime night mode state so that we don't need to notify
// anyone if the user decides to switch to the night mode to bedtime.
if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
@@ -1078,9 +1083,10 @@ final class UiModeManagerService extends SystemService {
Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
return;
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1108,9 +1114,10 @@ final class UiModeManagerService extends SystemService {
Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
return;
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1131,7 +1138,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mProjectionHolders == null) {
@@ -1177,7 +1184,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
return releaseProjectionUnchecked(projectionType, callingPackage);
}
@@ -1219,7 +1226,7 @@ final class UiModeManagerService extends SystemService {
return;
}
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mProjectionListeners == null) {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 12e8c57228d6..947f6b73d32a 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -48,7 +48,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.net.vcn.Flags;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -447,22 +446,16 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
- if (Flags.enforceMainUser()) {
- final UserManager userManager = mContext.getSystemService(UserManager.class);
-
- Binder.withCleanCallingIdentity(
- () -> {
- if (!Objects.equals(userManager.getMainUser(), userHandle)) {
- throw new SecurityException(
- "VcnManagementService can only be used by callers running as"
- + " the main user");
- }
- });
- } else if (!userHandle.isSystem()) {
- throw new SecurityException(
- "VcnManagementService can only be used by callers running as the primary user");
- }
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (!Objects.equals(userManager.getMainUser(), userHandle)) {
+ throw new SecurityException(
+ "VcnManagementService can only be used by callers running as"
+ + " the main user");
+ }
+ });
}
private void enforceCallingUserAndCarrierPrivilege(
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index f5a297bfd4f5..65a2c187f1c8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -246,7 +246,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
- private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
+ private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers();
private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
/**
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index adf0e640f6bf..d67fea09f945 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -225,3 +225,11 @@ flag {
description: "Migrate OomAdjuster pulled device state to a push model"
bug: "302575389"
}
+
+flag {
+ name: "oomadjuster_cached_app_tiers"
+ namespace: "system_performance"
+ is_fixed_read_only: true
+ description: "Assign cached oom_score_adj in tiers."
+ bug: "369893532"
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 020cef170e0c..37a2fba8fcb5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -69,6 +69,7 @@ import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
import static com.android.media.audio.Flags.replaceStreamBtSco;
import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
+import static com.android.media.audio.Flags.ringMyCar;
import static com.android.media.audio.Flags.setStreamVolumeOrder;
import static com.android.media.audio.Flags.vgsVssSyncMuteOrder;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
@@ -761,7 +762,7 @@ public class AudioService extends IAudioService.Stub
/** Streams that can be muted by system. Do not resolve to aliases when checking.
* @see System#MUTE_STREAMS_AFFECTED */
- private int mMuteAffectedStreams;
+ protected int mMuteAffectedStreams;
/** Streams that can be muted by user. Do not resolve to aliases when checking.
* @see System#MUTE_STREAMS_AFFECTED */
@@ -1465,7 +1466,8 @@ public class AudioService extends IAudioService.Stub
mPlaybackMonitor =
new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM],
- device -> onMuteAwaitConnectionTimeout(device));
+ device -> onMuteAwaitConnectionTimeout(device),
+ stream -> isStreamMute(stream));
mPlaybackMonitor.registerPlaybackCallback(mPlaybackActivityMonitor, true);
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
@@ -4846,6 +4848,8 @@ public class AudioService extends IAudioService.Stub
+ replaceStreamBtSco());
pw.println("\tcom.android.media.audio.equalScoLeaVcIndexRange:"
+ equalScoLeaVcIndexRange());
+ pw.println("\tcom.android.media.audio.ringMyCar:"
+ + ringMyCar());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -8695,9 +8699,14 @@ public class AudioService extends IAudioService.Stub
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when
// index is just set to 0 to repect BT requirements
+ boolean muted = false;
if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType)
&& getVssForStreamOrDefault(mPublicStreamType).isFullyMuted()) {
- index = 0;
+ if (ringMyCar()) {
+ muted = true;
+ } else {
+ index = 0;
+ }
} else if (isStreamBluetoothSco(mPublicStreamType) && index == 0) {
index = 1;
}
@@ -8707,13 +8716,14 @@ public class AudioService extends IAudioService.Stub
/ getVssForStreamOrDefault(mPublicStreamType).getIndexStepFactor());
}
+
if (DEBUG_VOL) {
Log.d(TAG, "setVolumeIndexInt(" + mAudioVolumeGroup.getId() + ", " + index + ", "
- + device + ")");
+ + muted + ", " + device + ")");
}
// Set the volume index
- mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, muted, device);
}
@GuardedBy("AudioService.VolumeStreamState.class")
@@ -9297,6 +9307,13 @@ public class AudioService extends IAudioService.Stub
}
}
+ /**
+ * Sends the new volume index on the given device to native.
+ *
+ * <p>Make sure the index is consistent with the muting state. When ringMyCar is enabled
+ * will send the non-zero index together with muted state. Otherwise, index 0 will be sent
+ * to native for signalising a muted stream.
+ **/
@GuardedBy("VolumeStreamState.class")
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
@@ -9311,18 +9328,19 @@ public class AudioService extends IAudioService.Stub
/ 10;
}
+ boolean muted = ringMyCar() ? isFullyMuted() : false;
if (DEBUG_VOL) {
- Log.d(TAG, "setStreamVolumeIndexAS(" + mStreamType + ", " + index + ", " + device
- + ")");
+ Log.d(TAG, "setStreamVolumeIndexAS(streamType=" + mStreamType + ", index=" + index
+ + ", muted=" + muted + ", device=" + device + ")");
}
- mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
+ mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, muted, device);
}
// must be called while synchronized VolumeStreamState.class
@GuardedBy("VolumeStreamState.class")
/*package*/ void applyDeviceVolume_syncVSS(int device) {
int index;
- if (isFullyMuted()) {
+ if (isFullyMuted() && !ringMyCar()) {
index = 0;
} else if (isAbsoluteVolumeDevice(device)
|| isA2dpAbsoluteVolumeDevice(device)
@@ -9356,7 +9374,7 @@ public class AudioService extends IAudioService.Stub
for (int i = 0; i < mIndexMap.size(); i++) {
final int device = mIndexMap.keyAt(i);
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
- if (isFullyMuted()) {
+ if (isFullyMuted() && !ringMyCar()) {
index = 0;
} else if (isAbsoluteVolumeDevice(device)
|| isA2dpAbsoluteVolumeDevice(device)
@@ -9391,7 +9409,7 @@ public class AudioService extends IAudioService.Stub
}
// apply default volume last: by convention , default device volume will be used
// by audio policy manager if no explicit volume is present for a given device type
- if (isFullyMuted()) {
+ if (isFullyMuted() && !ringMyCar()) {
index = 0;
} else {
index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 5cabddea9c17..e86c34cab88a 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -547,13 +547,14 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
* @param device
* @return
*/
- public int setStreamVolumeIndexAS(int stream, int index, int device) {
- return AudioSystem.setStreamVolumeIndexAS(stream, index, device);
+ public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
+ return AudioSystem.setStreamVolumeIndexAS(stream, index, muted, device);
}
/** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */
- public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
- return AudioSystem.setVolumeIndexForAttributes(attributes, index, device);
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted,
+ int device) {
+ return AudioSystem.setVolumeIndexForAttributes(attributes, index, muted, device);
}
/**
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a734e73d213b..b63b07f0453e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -72,6 +72,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Class to receive and dispatch updates from AudioSystem about recording configurations.
@@ -160,18 +161,22 @@ public final class PlaybackActivityMonitor
private final Context mContext;
private int mSavedAlarmVolume = -1;
+ private boolean mSavedAlarmMuted = false;
+ private final Function<Integer, Boolean> mIsStreamMutedCb;
private final int mMaxAlarmVolume;
private int mPrivilegedAlarmActiveCount = 0;
private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb;
private final FadeOutManager mFadeOutManager = new FadeOutManager();
PlaybackActivityMonitor(Context context, int maxAlarmVolume,
- Consumer<AudioDeviceAttributes> muteTimeoutCallback) {
+ Consumer<AudioDeviceAttributes> muteTimeoutCallback,
+ Function<Integer, Boolean> isStreamMutedCb) {
mContext = context;
mMaxAlarmVolume = maxAlarmVolume;
PlayMonitorClient.sListenerDeathMonitor = this;
AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback;
+ mIsStreamMutedCb = isStreamMutedCb;
initEventHandler();
}
@@ -332,8 +337,9 @@ public final class PlaybackActivityMonitor
if (mPrivilegedAlarmActiveCount++ == 0) {
mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
+ mSavedAlarmMuted = mIsStreamMutedCb.apply(AudioSystem.STREAM_ALARM);
AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
- mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ mMaxAlarmVolume, /*muted=*/false, AudioSystem.DEVICE_OUT_SPEAKER);
}
} else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
@@ -342,7 +348,8 @@ public final class PlaybackActivityMonitor
AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
mMaxAlarmVolume) {
AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
- mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ mSavedAlarmVolume, mSavedAlarmMuted,
+ AudioSystem.DEVICE_OUT_SPEAKER);
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index acf4db30ba93..0807c70d9922 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -292,6 +292,13 @@ final class DisplayDeviceInfo {
*/
public float renderFrameRate;
+
+ /**
+ * If {@code true}, this Display supports adaptive refresh rates.
+ * @see android.view.DisplayInfo#hasArrSupport for more details.
+ */
+ public boolean hasArrSupport;
+
/**
* The default mode of the display.
*/
@@ -540,7 +547,8 @@ final class DisplayDeviceInfo {
other.brightnessDefault)
|| !Objects.equals(roundedCorners, other.roundedCorners)
|| installOrientation != other.installOrientation
- || !Objects.equals(displayShape, other.displayShape)) {
+ || !Objects.equals(displayShape, other.displayShape)
+ || hasArrSupport != other.hasArrSupport) {
diff |= DIFF_OTHER;
}
return diff;
@@ -558,6 +566,7 @@ final class DisplayDeviceInfo {
height = other.height;
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
+ hasArrSupport = other.hasArrSupport;
defaultModeId = other.defaultModeId;
userPreferredModeId = other.userPreferredModeId;
supportedModes = other.supportedModes;
@@ -602,6 +611,7 @@ final class DisplayDeviceInfo {
sb.append(width).append(" x ").append(height);
sb.append(", modeId ").append(modeId);
sb.append(", renderFrameRate ").append(renderFrameRate);
+ sb.append(", hasArrSupport ").append(hasArrSupport);
sb.append(", defaultModeId ").append(defaultModeId);
sb.append(", userPreferredModeId ").append(userPreferredModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 5edea0a8b031..f9c3a46828b9 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -246,6 +246,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private int mActiveModeId = INVALID_MODE_ID;
private boolean mDisplayModeSpecsInvalid;
private int mActiveColorMode;
+ private boolean mHasArrSupport;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mAllmSupported;
private boolean mGameContentTypeSupported;
@@ -311,6 +312,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities);
changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported);
changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported);
+ changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport);
if (changed) {
mHavePendingChanges = true;
@@ -602,6 +604,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
+ private boolean updateHasArrSupportLocked(boolean newHasArrSupport) {
+ if (mHasArrSupport == newHasArrSupport) {
+ return false;
+ }
+ mHasArrSupport = newHasArrSupport;
+ return true;
+ }
+
private boolean updateAllmSupport(boolean supported) {
if (mAllmSupported == supported) {
return false;
@@ -684,6 +694,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
+ mInfo.hasArrSupport = mHasArrSupport;
mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
mInfo.state = mState;
@@ -1274,6 +1285,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
pw.println("mUserPreferredModeId=" + mUserPreferredModeId);
+ pw.println("mHasArrSupport=" + mHasArrSupport);
pw.println("mState=" + Display.stateToString(mState));
pw.println("mCommittedState=" + Display.stateToString(mCommittedState));
pw.println("mBrightnessState=" + mBrightnessState);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 007e3a8fde2f..074a4d851aef 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -506,12 +506,13 @@ final class LogicalDisplay {
mBaseDisplayInfo.rotation = Surface.ROTATION_0;
mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
+ mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
deviceInfo.supportedModes, deviceInfo.supportedModes.length);
mBaseDisplayInfo.appsSupportedModes = syntheticModeManager.createAppSupportedModes(
- config, mBaseDisplayInfo.supportedModes
+ config, mBaseDisplayInfo.supportedModes, mBaseDisplayInfo.hasArrSupport
);
mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
mBaseDisplayInfo.supportedColorModes = Arrays.copyOf(
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index 9a0ee034a8f2..a4804e1887fe 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -50,8 +50,10 @@ public final class BrightnessReason {
public static final int MODIFIER_THROTTLED = 0x8;
public static final int MODIFIER_MIN_LUX = 0x10;
public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20;
+ public static final int MODIFIER_STYLUS_UNDER_USE = 0x40;
public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
- | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND;
+ | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND
+ | MODIFIER_STYLUS_UNDER_USE;
// ADJUSTMENT_*
// These things can happen at any point, even if the main brightness reason doesn't
@@ -158,6 +160,9 @@ public final class BrightnessReason {
if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) {
sb.append(" user_min_pref");
}
+ if ((mModifier & MODIFIER_STYLUS_UNDER_USE) != 0) {
+ sb.append(" stylus_under_use");
+ }
int strlen = sb.length();
if (sb.charAt(strlen - 1) == '[') {
sb.setLength(strlen - 2);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 71fdaf3f85b6..4bd980822e46 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -110,6 +110,9 @@ public final class DisplayBrightnessController {
@VisibleForTesting
AutomaticBrightnessController mAutomaticBrightnessController;
+ // True if the stylus is being used
+ private boolean mIsStylusBeingUsed;
+
/**
* The constructor of DisplayBrightnessController.
*/
@@ -460,6 +463,8 @@ public final class DisplayBrightnessController {
writer.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault);
writer.println(" mPersistBrightnessNitsForDefaultDisplay="
+ mPersistBrightnessNitsForDefaultDisplay);
+ writer.println(" mIsStylusBeingUsed="
+ + mIsStylusBeingUsed);
synchronized (mLock) {
writer.println(" mPendingScreenBrightness=" + mPendingScreenBrightness);
writer.println(" mCurrentScreenBrightness=" + mCurrentScreenBrightness);
@@ -505,7 +510,12 @@ public final class DisplayBrightnessController {
* Notifies if the stylus is currently being used or not.
*/
public void setStylusBeingUsed(boolean isEnabled) {
- // Todo(b/369977976) - Disable the auto-brightness strategy
+ mIsStylusBeingUsed = isEnabled;
+ }
+
+ @VisibleForTesting
+ boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
}
@VisibleForTesting
@@ -626,13 +636,14 @@ public final class DisplayBrightnessController {
lastUserSetScreenBrightness = mLastUserSetScreenBrightness;
}
return new StrategySelectionRequest(displayPowerRequest, targetDisplayState,
- lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession);
+ lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession,
+ mIsStylusBeingUsed);
}
private StrategyExecutionRequest constructStrategyExecutionRequest(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
float currentScreenBrightness = getCurrentBrightness();
return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
- mUserSetScreenBrightnessUpdated);
+ mUserSetScreenBrightnessUpdated, mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 06890e72f068..ded7447c5fbc 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -306,7 +306,8 @@ public class DisplayBrightnessStrategySelector {
strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze,
strategySelectionRequest.getLastUserSetScreenBrightness(),
strategySelectionRequest.isUserSetBrightnessChanged());
- return mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
+ return !strategySelectionRequest.isStylusBeingUsed()
+ && mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
}
private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest(
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 304640b884ef..7a07c4fc22bf 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -32,11 +32,15 @@ public final class StrategyExecutionRequest {
// Represents if the user set screen brightness was changed or not.
private boolean mUserSetBrightnessChanged;
+ private boolean mIsStylusBeingUsed;
+
public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
- float currentScreenBrightness, boolean userSetBrightnessChanged) {
+ float currentScreenBrightness, boolean userSetBrightnessChanged,
+ boolean isStylusBeingUsed) {
mDisplayPowerRequest = displayPowerRequest;
mCurrentScreenBrightness = currentScreenBrightness;
mUserSetBrightnessChanged = userSetBrightnessChanged;
+ mIsStylusBeingUsed = isStylusBeingUsed;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -51,6 +55,10 @@ public final class StrategyExecutionRequest {
return mUserSetBrightnessChanged;
}
+ public boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategyExecutionRequest)) {
@@ -59,12 +67,13 @@ public final class StrategyExecutionRequest {
StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
&& mCurrentScreenBrightness == other.getCurrentScreenBrightness()
- && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
+ && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
+ && mIsStylusBeingUsed == other.isStylusBeingUsed();
}
@Override
public int hashCode() {
return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
- mUserSetBrightnessChanged);
+ mUserSetBrightnessChanged, mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
index aa2f23ef9ec1..5c1f03d877a6 100644
--- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
@@ -40,15 +40,19 @@ public final class StrategySelectionRequest {
private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+ private boolean mIsStylusBeingUsed;
+
public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
int targetDisplayState, float lastUserSetScreenBrightness,
boolean userSetBrightnessChanged,
- DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
+ DisplayManagerInternal.DisplayOffloadSession displayOffloadSession,
+ boolean isStylusBeingUsed) {
mDisplayPowerRequest = displayPowerRequest;
mTargetDisplayState = targetDisplayState;
mLastUserSetScreenBrightness = lastUserSetScreenBrightness;
mUserSetBrightnessChanged = userSetBrightnessChanged;
mDisplayOffloadSession = displayOffloadSession;
+ mIsStylusBeingUsed = isStylusBeingUsed;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -72,6 +76,10 @@ public final class StrategySelectionRequest {
return mDisplayOffloadSession;
}
+ public boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategySelectionRequest)) {
@@ -82,12 +90,14 @@ public final class StrategySelectionRequest {
&& mTargetDisplayState == other.getTargetDisplayState()
&& mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness()
&& mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
- && mDisplayOffloadSession.equals(other.getDisplayOffloadSession());
+ && mDisplayOffloadSession.equals(other.getDisplayOffloadSession())
+ && mIsStylusBeingUsed == other.isStylusBeingUsed();
}
@Override
public int hashCode() {
return Objects.hash(mDisplayPowerRequest, mTargetDisplayState,
- mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession);
+ mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession,
+ mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 7c0c9312888e..b9de34a80bda 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -37,6 +37,9 @@ public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{
StrategyExecutionRequest strategyExecutionRequest) {
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+ if (strategyExecutionRequest.isStylusBeingUsed()) {
+ brightnessReason.setModifier(BrightnessReason.MODIFIER_STYLUS_UNDER_USE);
+ }
return new DisplayBrightnessState.Builder()
.setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
.setBrightnessReason(brightnessReason)
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 70bf56646b32..07343f469ed7 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -232,6 +232,11 @@ public class DisplayManagerFlags {
Flags::enableBatteryStatsForAllDisplays
);
+ private final FlagState mHasArrSupport = new FlagState(
+ Flags.FLAG_ENABLE_HAS_ARR_SUPPORT,
+ Flags::enableHasArrSupport
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -493,6 +498,12 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if hasArrSupport API is enabled.
+ */
+ public boolean hasArrSupportFlag() {
+ return mHasArrSupport.isEnabled();
+ }
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -541,6 +552,7 @@ public class DisplayManagerFlags {
pw.println(" " + mEnableApplyDisplayChangedDuringDisplayAdded);
pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage);
pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled);
+ pw.println(" " + mHasArrSupport);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 602c6997b603..ddb29691f42e 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -414,3 +414,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_has_arr_support"
+ namespace: "core_graphics"
+ description: "Flag for an API to get whether display supports ARR or not"
+ bug: "361433651"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index ffa64bfcf29f..88562ab9ba2d 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -156,6 +156,8 @@ public class DisplayModeDirector {
// a map from display id to display device config
private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>();
+ private SparseBooleanArray mHasArrSupport;
+
private BrightnessObserver mBrightnessObserver;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
@@ -194,6 +196,8 @@ public class DisplayModeDirector {
private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled;
+ private final boolean mHasArrSupportFlagEnabled;
+
private final DisplayManagerFlags mDisplayManagerFlags;
private final DisplayDeviceConfigProvider mDisplayDeviceConfigProvider;
@@ -218,6 +222,7 @@ public class DisplayModeDirector {
.isDisplaysRefreshRatesSynchronizationEnabled();
mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
+ mHasArrSupportFlagEnabled = displayManagerFlags.hasArrSupportFlag();
mDisplayManagerFlags = displayManagerFlags;
mDisplayDeviceConfigProvider = displayDeviceConfigProvider;
mContext = context;
@@ -228,6 +233,7 @@ public class DisplayModeDirector {
mSupportedModesByDisplay = new SparseArray<>();
mAppSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
+ mHasArrSupport = new SparseBooleanArray();
mAppRequestObserver = new AppRequestObserver(displayManagerFlags);
mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
@@ -452,7 +458,13 @@ public class DisplayModeDirector {
return mAppRequestObserver;
}
+ // TODO(b/372019752) Rename all the occurrences of the VRR with ARR.
private boolean isVrrSupportedLocked(int displayId) {
+ if (mHasArrSupportFlagEnabled) {
+ Boolean hasArrSupport = mHasArrSupport.get(displayId);
+ return hasArrSupport != null && hasArrSupport;
+ }
+ // TODO(b/371041638) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out
DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.get(displayId);
return config != null && config.isVrrSupportEnabled();
}
@@ -1469,6 +1481,7 @@ public class DisplayModeDirector {
DisplayInfo displayInfo = getDisplayInfo(displayId);
registerExternalDisplay(displayInfo);
updateDisplayModes(displayId, displayInfo);
+ updateHasArrSupport(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
updateDisplaysPeakRefreshRateAndResolution(displayInfo);
@@ -1482,6 +1495,7 @@ public class DisplayModeDirector {
mDefaultModeByDisplay.remove(displayId);
mDisplayDeviceConfigByDisplay.remove(displayId);
mSettingsObserver.removeRefreshRateSetting(displayId);
+ mHasArrSupport.delete(displayId);
}
updateLayoutLimitedFrameRate(displayId, null);
removeUserSettingDisplayPreferredSize(displayId);
@@ -1493,6 +1507,7 @@ public class DisplayModeDirector {
public void onDisplayChanged(int displayId) {
updateDisplayDeviceConfig(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
+ updateHasArrSupport(displayId, displayInfo);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
@@ -1691,6 +1706,16 @@ public class DisplayModeDirector {
}
}
}
+
+ private void updateHasArrSupport(int displayId, @Nullable DisplayInfo info) {
+ if (info == null) {
+ return;
+ }
+ synchronized (mLock) {
+ mHasArrSupport.put(displayId, info.hasArrSupport);
+ }
+ }
+
}
/**
diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
index a83b9395dfc0..71b34679882d 100644
--- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
+++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
@@ -37,17 +37,22 @@ public class SyntheticModeManager {
SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;
private final boolean mSynthetic60HzModesEnabled;
+ private final boolean mHasArrSupportEnabled;
public SyntheticModeManager(DisplayManagerFlags flags) {
mSynthetic60HzModesEnabled = flags.isSynthetic60HzModesEnabled();
+ mHasArrSupportEnabled = flags.hasArrSupportFlag();
}
/**
* creates display supportedModes array, exposed to applications
*/
public Display.Mode[] createAppSupportedModes(DisplayDeviceConfig config,
- Display.Mode[] modes) {
- if (!config.isVrrSupportEnabled() || !mSynthetic60HzModesEnabled) {
+ Display.Mode[] modes, boolean hasArrSupport) {
+ // TODO(b/361433651) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out
+ boolean isArrSupported =
+ mHasArrSupportEnabled ? hasArrSupport : config.isVrrSupportEnabled();
+ if (!isArrSupported || !mSynthetic60HzModesEnabled) {
return modes;
}
List<Display.Mode> appSupportedModes = new ArrayList<>();
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index dc2c95797b8b..636854b85ee4 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -63,7 +63,6 @@ import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
-import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.pm.PackageManagerServiceUtils;
@@ -130,7 +129,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private final Handler mHandler;
private final PackageManagerInternal mPackageManagerInternal;
private final Supplier<PackageParser2> mParserSupplier;
- private final RuleEvaluationEngine mEvaluationEngine;
private final IntegrityFileManager mIntegrityFileManager;
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
@@ -142,7 +140,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
context,
LocalServices.getService(PackageManagerInternal.class),
PackageParserUtils::forParsingFileWithDefaults,
- RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
handlerThread.getThreadHandler());
}
@@ -152,13 +149,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
Context context,
PackageManagerInternal packageManagerInternal,
Supplier<PackageParser2> parserSupplier,
- RuleEvaluationEngine evaluationEngine,
IntegrityFileManager integrityFileManager,
Handler handler) {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
mParserSupplier = parserSupplier;
- mEvaluationEngine = evaluationEngine;
mIntegrityFileManager = integrityFileManager;
mHandler = handler;
@@ -330,7 +325,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
+ " installers "
+ allowedInstallers);
}
- IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
+ IntegrityCheckResult result = IntegrityCheckResult.allow();
if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) {
Slog.i(
TAG,
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
deleted file mode 100644
index e8c828bb85b6..000000000000
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.Rule;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.integrity.IntegrityFileManager;
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The engine used to evaluate rules against app installs.
- *
- * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine
- * to allow/block that install.
- */
-public class RuleEvaluationEngine {
- private static final String TAG = "RuleEvaluation";
-
- // The engine for loading rules, retrieving metadata for app installs, and evaluating app
- // installs against rules.
- private static RuleEvaluationEngine sRuleEvaluationEngine;
-
- private final IntegrityFileManager mIntegrityFileManager;
-
- @VisibleForTesting
- RuleEvaluationEngine(IntegrityFileManager integrityFileManager) {
- mIntegrityFileManager = integrityFileManager;
- }
-
- /** Provide a singleton instance of the rule evaluation engine. */
- public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() {
- if (sRuleEvaluationEngine == null) {
- return new RuleEvaluationEngine(IntegrityFileManager.getInstance());
- }
- return sRuleEvaluationEngine;
- }
-
- /**
- * Load, and match the list of rules against an app install metadata.
- *
- * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
- * against.
- * @return result of the integrity check
- */
- public IntegrityCheckResult evaluate(
- AppInstallMetadata appInstallMetadata) {
- List<Rule> rules = loadRules(appInstallMetadata);
- return IntegrityCheckResult.allow();
- }
-
- private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
- if (!mIntegrityFileManager.initialized()) {
- Slog.w(TAG, "Integrity rule files are not available.");
- return Collections.emptyList();
- }
-
- try {
- return mIntegrityFileManager.readRules(appInstallMetadata);
- } catch (Exception e) {
- Slog.e(TAG, "Error loading rules.", e);
- return new ArrayList<>();
- }
- }
-}
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index c79f41d32b29..25e1c94aa689 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -420,7 +420,7 @@ import java.util.Objects;
// to derive a name ourselves from the type instead.
String deviceName = audioDeviceInfo.getPort().name();
- if (!TextUtils.isEmpty(address)) {
+ if (mBluetoothRouteController.containsBondedDeviceWithAddress(address)) {
routeId = mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
deviceName = mBluetoothRouteController.getNameForBluetoothAddress(address);
}
diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index 8b65ea305ad8..c79d6e5400cd 100644
--- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -141,6 +141,11 @@ import java.util.stream.Collectors;
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
+ /** Returns true if the given address corresponds to a currently-bonded Bluetooth device. */
+ public synchronized boolean containsBondedDeviceWithAddress(@Nullable String address) {
+ return mAddressToBondedDevice.containsKey(address);
+ }
+
@Nullable
public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) {
BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3ba93845a290..93f512bc7e17 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -334,7 +334,7 @@ public final class NotificationRecord {
return helper.createWaveformVibration(vibrationPattern, insistent);
}
- if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+ if (com.android.server.notification.Flags.notificationVibrationInSoundUriForChannel()) {
final VibrationEffect vibrationEffectFromSoundUri =
helper.createVibrationEffectFromSoundUri(channel.getSound());
if (vibrationEffectFromSoundUri != null) {
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 49a6ffde6783..a221152222ee 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -55,6 +55,8 @@ public class BackgroundUserSoundNotifier {
private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
private static final String ACTION_DISMISS_NOTIFICATION =
"com.android.server.ACTION_DISMISS_NOTIFICATION";
+ private static final String EXTRA_NOTIFICATION_CLIENT_UID =
+ "com.android.server.EXTRA_CLIENT_UID";
/**
* The clientUid from the AudioFocusInfo of the background user,
* for which an active notification is currently displayed.
@@ -101,7 +103,7 @@ public class BackgroundUserSoundNotifier {
ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);
registerReceiver(am);
- mBgUserListener = new BackgroundUserListener(mSystemUserContext);
+ mBgUserListener = new BackgroundUserListener();
AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
focusControlPolicyBuilder.setLooper(Looper.getMainLooper());
@@ -119,26 +121,16 @@ public class BackgroundUserSoundNotifier {
final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener {
- Context mSystemContext;
-
- BackgroundUserListener(Context systemContext) {
- mSystemContext = systemContext;
- }
-
- @SuppressLint("MissingPermission")
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
try {
- BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi,
- mSystemContext.createContextAsUser(
- UserHandle.of(ActivityManager.getCurrentUser()), 0));
+ BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
- @SuppressLint("MissingPermission")
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
- BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary();
+ BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi.getClientUid());
}
}
@@ -160,7 +152,7 @@ public class BackgroundUserSoundNotifier {
if (mNotificationClientUid == -1) {
return;
}
- dismissNotification();
+ dismissNotification(mNotificationClientUid);
if (DEBUG) {
final int actionIndex = intent.getAction().lastIndexOf(".") + 1;
@@ -171,7 +163,7 @@ public class BackgroundUserSoundNotifier {
}
if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
- muteAlarmSounds(mSystemUserContext);
+ muteAlarmSounds(mNotificationClientUid);
} else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid));
}
@@ -193,17 +185,17 @@ public class BackgroundUserSoundNotifier {
*/
@SuppressLint("MissingPermission")
@VisibleForTesting
- void muteAlarmSounds(Context context) {
- AudioManager audioManager = context.getSystemService(AudioManager.class);
+ void muteAlarmSounds(int notificationClientUid) {
+ AudioManager audioManager = mSystemUserContext.getSystemService(AudioManager.class);
if (audioManager != null) {
for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) {
- if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) {
+ if (apc.getClientUid() == notificationClientUid && apc.getPlayerProxy() != null) {
apc.getPlayerProxy().stop();
}
}
}
- AudioFocusInfo currentAfi = getAudioFocusInfoForNotification();
+ AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(notificationClientUid);
if (currentAfi != null) {
mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi);
}
@@ -212,9 +204,14 @@ public class BackgroundUserSoundNotifier {
/**
* Check if sound is coming from background user and show notification is required.
*/
+ @SuppressLint("MissingPermission")
@VisibleForTesting
- void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext)
- throws RemoteException {
+ void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi) throws RemoteException {
+ if (afi == null) {
+ return;
+ }
+ Context foregroundContext = mSystemUserContext.createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), 0);
final int userId = UserHandle.getUserId(afi.getClientUid());
final int usage = afi.getAttributes().getUsage();
UserInfo userInfo = mUserManager.getUserInfo(userId);
@@ -232,8 +229,8 @@ public class BackgroundUserSoundNotifier {
mNotificationClientUid = afi.getClientUid();
- mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid,
- createNotification(userInfo.name, foregroundContext),
+ mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
+ createNotification(userInfo.name, foregroundContext, afi.getClientUid()),
foregroundContext.getUser());
}
}
@@ -245,14 +242,15 @@ public class BackgroundUserSoundNotifier {
* focus ownership.
*/
@VisibleForTesting
- void dismissNotificationIfNecessary() {
- if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) {
+ void dismissNotificationIfNecessary(int notificationClientUid) {
+ if (getAudioFocusInfoForNotification(notificationClientUid) == null
+ && mNotificationClientUid >= 0) {
if (DEBUG) {
Log.d(LOG_TAG, "Alarm ringing on background user "
- + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier()
+ + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier()
+ " left focus stack, dismissing notification");
}
- dismissNotification();
+ dismissNotification(notificationClientUid);
mNotificationClientUid = -1;
}
}
@@ -262,8 +260,8 @@ public class BackgroundUserSoundNotifier {
* shown.
*/
@SuppressLint("MissingPermission")
- private void dismissNotification() {
- mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL);
+ private void dismissNotification(int notificationClientUid) {
+ mNotificationManager.cancelAsUser(LOG_TAG, notificationClientUid, UserHandle.ALL);
}
/**
@@ -272,11 +270,11 @@ public class BackgroundUserSoundNotifier {
@SuppressLint("MissingPermission")
@VisibleForTesting
@Nullable
- AudioFocusInfo getAudioFocusInfoForNotification() {
- if (mNotificationClientUid >= 0) {
+ AudioFocusInfo getAudioFocusInfoForNotification(int notificationClientUid) {
+ if (notificationClientUid >= 0) {
List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack();
for (int i = stack.size() - 1; i >= 0; i--) {
- if (stack.get(i).getClientUid() == mNotificationClientUid) {
+ if (stack.get(i).getClientUid() == notificationClientUid) {
return stack.get(i);
}
}
@@ -284,22 +282,24 @@ public class BackgroundUserSoundNotifier {
return null;
}
- private PendingIntent createPendingIntent(String intentAction) {
+ private PendingIntent createPendingIntent(String intentAction, int notificationClientUid) {
final Intent intent = new Intent(intentAction);
- PendingIntent resultPI = PendingIntent.getBroadcast(mSystemUserContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- return resultPI;
+ intent.putExtra(EXTRA_NOTIFICATION_CLIENT_UID, notificationClientUid);
+ return PendingIntent.getBroadcast(mSystemUserContext, notificationClientUid, intent,
+ PendingIntent.FLAG_IMMUTABLE);
}
+ @SuppressLint("MissingPermission")
@VisibleForTesting
- Notification createNotification(String userName, Context fgContext) {
+ Notification createNotification(String userName, Context fgContext, int notificationClientUid) {
final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
userName);
final int icon = R.drawable.ic_audio_alarm;
- PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND);
- PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER);
- PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION);
+ PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND, notificationClientUid);
+ PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER, notificationClientUid);
+ PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION,
+ notificationClientUid);
final Notification.Action mute = new Notification.Action.Builder(null,
fgContext.getString(R.string.bg_user_sound_notification_button_mute),
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 02afdd662b10..9e8598a04a98 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -89,6 +89,7 @@ import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -893,7 +894,8 @@ public final class DexOptHelper {
@Override
public void onApexStaged(@NonNull ApexStagedEvent event) {
- mArtManager.onApexStaged(event.stagedApexModuleNames);
+ mArtManager.onApexStaged(Arrays.stream(event.stagedApexInfos)
+ .map(info -> info.moduleName).toArray(String[]::new));
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
index 66ecd6e67e56..7d8573e35522 100644
--- a/services/core/java/com/android/server/pm/PackageManagerNative.java
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -20,7 +20,6 @@ import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static com.android.server.pm.PackageManagerService.TAG;
-import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IStagedApexObserver;
@@ -184,14 +183,8 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
}
@Override
- public String[] getStagedApexModuleNames() {
- return mPm.mInstallerService.getStagingManager()
- .getStagedApexModuleNames().toArray(new String[0]);
- }
-
- @Override
- @Nullable
- public StagedApexInfo getStagedApexInfo(String moduleName) {
- return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+ public StagedApexInfo[] getStagedApexInfos() {
+ return mPm.mInstallerService.getStagingManager().getStagedApexInfos().toArray(
+ new StagedApexInfo[0]);
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 94bdfbd9c6f5..38cf0b9136dd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,7 +17,6 @@
package com.android.server.pm;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.apex.ApexSessionInfo;
import android.apex.ApexSessionParams;
@@ -38,7 +37,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -65,9 +63,9 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -807,14 +805,13 @@ public class StagingManager {
}
/**
- * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>},
- * where the key of the map is the module name of the ApexInfo.
+ * Returns ApexInfo about APEX contained inside the session.
*
- * Returns an empty map if there is any error.
+ * Returns an empty list if there is any error.
*/
@VisibleForTesting
@NonNull
- Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
+ List<ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
Preconditions.checkArgument(session != null, "Session is null");
Preconditions.checkArgument(!session.hasParentSessionId(),
session.sessionId() + " session has parent session");
@@ -824,7 +821,7 @@ public class StagingManager {
// Even if caller calls this method on ready session, the session could be abandoned
// right after this method is called.
if (!session.isSessionReady() || session.isDestroyed()) {
- return Collections.emptyMap();
+ return Collections.emptyList();
}
ApexSessionParams params = new ApexSessionParams();
@@ -838,38 +835,17 @@ public class StagingManager {
}
}
params.childSessionIds = childSessionIds.toArray();
-
- ApexInfo[] infos = mApexManager.getStagedApexInfos(params);
- Map<String, ApexInfo> result = new ArrayMap<>();
- for (ApexInfo info : infos) {
- result.put(info.moduleName, info);
- }
- return result;
+ return Arrays.asList(mApexManager.getStagedApexInfos(params));
}
/**
- * Returns apex module names of all packages that are staged ready
- */
- List<String> getStagedApexModuleNames() {
- List<String> result = new ArrayList<>();
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- final StagedSession session = mStagedSessions.valueAt(i);
- if (!session.isSessionReady() || session.isDestroyed()
- || session.hasParentSessionId() || !session.containsApexSession()) {
- continue;
- }
- result.addAll(getStagedApexInfos(session).keySet());
- }
- }
- return result;
- }
-
- /**
- * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null.
+ * Returns ApexInfo list about APEXes contained inside all staged sessions.
+ *
+ * Returns an empty list if there is any error.
*/
- @Nullable
- StagedApexInfo getStagedApexInfo(String moduleName) {
+ @NonNull
+ List<StagedApexInfo> getStagedApexInfos() {
+ List<StagedApexInfo> result = new ArrayList<>();
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final StagedSession session = mStagedSessions.valueAt(i);
@@ -877,8 +853,7 @@ public class StagingManager {
|| session.hasParentSessionId() || !session.containsApexSession()) {
continue;
}
- ApexInfo ai = getStagedApexInfos(session).get(moduleName);
- if (ai != null) {
+ getStagedApexInfos(session).stream().map(ai -> {
StagedApexInfo info = new StagedApexInfo();
info.moduleName = ai.moduleName;
info.diskImagePath = ai.modulePath;
@@ -886,17 +861,19 @@ public class StagingManager {
info.versionName = ai.versionName;
info.hasClassPathJars = ai.hasClassPathJars;
return info;
- }
+ }).forEach(result::add);
}
}
- return null;
+ return result;
}
private void notifyStagedApexObservers() {
synchronized (mStagedApexObservers) {
+ List<StagedApexInfo> stagedApexInfos = getStagedApexInfos();
+ ApexStagedEvent event = new ApexStagedEvent();
+ event.stagedApexInfos =
+ stagedApexInfos.toArray(new StagedApexInfo[stagedApexInfos.size()]);
for (IStagedApexObserver observer : mStagedApexObservers) {
- ApexStagedEvent event = new ApexStagedEvent();
- event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
try {
observer.onApexStaged(event);
} catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index eb62b5631c43..8ba56c5320f2 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -771,6 +771,10 @@ public class Notifier {
public void onGroupRemoved(int groupId) {
mInteractivityByGroupId.remove(groupId);
mWakefulnessSessionObserver.removePowerGroup(groupId);
+ if (mFlags.isPerDisplayWakeByTouchEnabled()) {
+ resetDisplayInteractivities();
+ mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities);
+ }
}
/**
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 5f704a002a33..6f1e15b5033f 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -29,7 +29,6 @@ import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.IpSecTransformState;
import android.net.Network;
-import android.net.vcn.Flags;
import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -233,7 +232,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) {
int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
- if (Flags.handleSeqNumLeap() && carrierConfig != null) {
+ if (carrierConfig != null) {
maxSeqNumIncrease =
carrierConfig.getInt(
VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
@@ -287,10 +286,8 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
// with the new interval
mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
- if (Flags.handleSeqNumLeap()) {
- mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
- mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
- }
+ mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+ mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
if (canStart() != isStarted()) {
if (canStart()) {
@@ -438,13 +435,10 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
onValidationResultReceivedInternal(true /* isFailed */);
}
- // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation
- if (Flags.validateNetworkOnIpsecLoss()) {
- // Trigger re-validation of the underlying network; if it fails, the VCN will
- // attempt to migrate away.
- mConnectivityManager.reportNetworkConnectivity(
- getNetwork(), false /* hasConnectivity */);
- }
+ // In both "invalid" and "unusual_seq_num_leap" cases, trigger network validation. If
+ // validation fails, the VCN will attempt to migrate away.
+ mConnectivityManager.reportNetworkConnectivity(
+ getNetwork(), false /* hasConnectivity */);
}
}
@@ -474,8 +468,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
boolean isUnusualSeqNumLeap = false;
// Handle sequence number leap
- if (Flags.handleSeqNumLeap()
- && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+ if (maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
final long timeDiffMillis =
newState.getTimestampMillis() - oldState.getTimestampMillis();
final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000;
@@ -506,7 +499,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
+ " actualPktCntDiff: "
+ actualPktCntDiff);
- if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
+ if (expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
// The sample size is too small to ensure a reliable detection result
return PacketLossCalculationResult.invalid();
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 78e06d46c74c..c852fb4e170f 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -25,7 +25,6 @@ import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.vcn.Flags;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
@@ -297,10 +296,8 @@ public class UnderlyingNetworkEvaluator {
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
- if (Flags.evaluateIpsecLossOnLpNcChange()) {
- for (NetworkMetricMonitor monitor : mMetricMonitors) {
- monitor.onLinkPropertiesOrCapabilitiesChanged();
- }
+ for (NetworkMetricMonitor monitor : mMetricMonitors) {
+ monitor.onLinkPropertiesOrCapabilitiesChanged();
}
}
@@ -316,10 +313,8 @@ public class UnderlyingNetworkEvaluator {
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
- if (Flags.evaluateIpsecLossOnLpNcChange()) {
- for (NetworkMetricMonitor monitor : mMetricMonitors) {
- monitor.onLinkPropertiesOrCapabilitiesChanged();
- }
+ for (NetworkMetricMonitor monitor : mMetricMonitors) {
+ monitor.onLinkPropertiesOrCapabilitiesChanged();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a44eb48d7836..460de01a7d1d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -285,9 +285,6 @@ import android.app.servertransaction.StopActivityItem;
import android.app.servertransaction.TopResumedActivityChangeItem;
import android.app.servertransaction.TransferSplashScreenViewStateItem;
import android.app.usage.UsageEvents.Event;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -467,11 +464,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// finished destroying itself.
private static final int DESTROY_TIMEOUT = 10 * 1000;
- @ChangeId
- @Overridable
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415;
-
final ActivityTaskManagerService mAtmService;
final ActivityCallerState mCallerState;
@NonNull
@@ -3192,7 +3184,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& mDisplayContent != null && mDisplayContent.getConfiguration()
.smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
&& mDisplayContent.getIgnoreOrientationRequest()
- && info.isChangeEnabled(UNIVERSAL_RESIZABLE_BY_DEFAULT);
+ && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 87fa62ac0e3b..5b5bb88cac98 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -86,6 +86,7 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENS
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;
@@ -2786,10 +2787,22 @@ class ActivityStarter {
}
}
- if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
- && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
- // ignore the flag if there is no the sourceRecord or without new_task flag
- mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ final boolean hasNewTaskFlag = (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0;
+ if (!hasNewTaskFlag || mSourceRecord == null) {
+ // ignore the flag if there is no the sourceRecord or without new_task flag
+ Slog.w(TAG_WM, !hasNewTaskFlag
+ ? "Launch adjacent ignored due to missing NEW_TASK"
+ : "Launch adjacent ignored due to missing source activity");
+ mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ }
+ // Ensure that the source task or its parents has not disabled launch-adjacent
+ if (mSourceRecord != null && mSourceRecord.getTask() != null &&
+ mSourceRecord.getTask().isLaunchAdjacentDisabled()) {
+ Slog.w(TAG_WM, "Launch adjacent blocked by source task or ancestor");
+ mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ }
+
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3d6b64b2e536..3560565ce9cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -589,7 +589,7 @@ public abstract class ActivityTaskManagerInternal {
* sensitive environment.
*/
public abstract TaskSnapshot getTaskSnapshotBlocking(int taskId,
- boolean isLowResolution);
+ boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage);
/** Returns true if uid is considered foreground for activity start purposes. */
public abstract boolean isUidForeground(int uid);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3dfc8f4e5bf9..4db478a13c92 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3946,6 +3946,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ private TaskSnapshot getTaskSnapshotInner(int taskId, boolean isLowResolution,
+ @TaskSnapshot.ReferenceFlags int usage) {
+ final Task task;
+ synchronized (mGlobalLock) {
+ task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
+ if (task == null) {
+ Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+ return null;
+ }
+ // Try to load snapshot from cache first, and add reference if the snapshot is in cache.
+ final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ task.mUserId, false /* restoreFromDisk */, isLowResolution);
+ if (snapshot != null) {
+ snapshot.addReference(usage);
+ return snapshot;
+ }
+ }
+ // Don't call this while holding the lock as this operation might hit the disk.
+ return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ task.mUserId, true /* restoreFromDisk */, isLowResolution);
+ }
+
@Override
public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
@@ -7274,8 +7296,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public TaskSnapshot getTaskSnapshotBlocking(
- int taskId, boolean isLowResolution) {
- return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution);
+ int taskId, boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) {
+ return ActivityTaskManagerService.this.getTaskSnapshotInner(
+ taskId, isLowResolution, usage);
}
@Override
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 515f148ac2ff..a380ba1a6f11 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -558,7 +558,7 @@ public class BackgroundActivityStartController {
.append(mBalAllowedByPiCreatorWithHardening);
sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
sb.append("; callerStartMode: ").append(balStartModeToString(
- mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
+ mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
sb.append("; hasRealCaller: ").append(hasRealCaller());
sb.append("; isCallForResult: ").append(mIsCallForResult);
sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -585,7 +585,7 @@ public class BackgroundActivityStartController {
sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
sb.append("; realCallerStartMode: ").append(balStartModeToString(
- mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
+ mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
}
// features
sb.append("; balImproveRealCallerVisibilityCheck: ")
@@ -792,7 +792,8 @@ public class BackgroundActivityStartController {
// Allowed before V by creator
if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked"
- + " if the PI creator upgrades target_sdk to 35+! "
+ + " if the PI creator upgrades target_sdk to 35+!"
+ + " goo.gle/android-bal"
+ " (missing opt in by PI creator)!" + state);
return allowBasedOnCaller(state);
}
@@ -802,6 +803,7 @@ public class BackgroundActivityStartController {
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked"
+ " if the PI sender upgrades target_sdk to 34+! "
+ + " goo.gle/android-bal"
+ " (missing opt in by PI sender)!" + state);
return allowBasedOnRealCaller(state);
}
@@ -829,7 +831,8 @@ public class BackgroundActivityStartController {
}
private BalVerdict abortLaunch(BalState state) {
- Slog.wtf(TAG, "Background activity launch blocked! " + state);
+ Slog.wtf(TAG, "Background activity launch blocked! goo.gle/android-bal "
+ + state);
if (balShowToastsBlocked()
&& (state.mResultForCaller.allows() || state.mResultForRealCaller.allows())) {
// only show a toast if either caller or real caller could launch if they opted in
@@ -1044,6 +1047,24 @@ public class BackgroundActivityStartController {
"realCallingUid has BAL permission.");
}
+ // don't abort if the realCallingUid has SYSTEM_ALERT_WINDOW permission
+ Slog.i(TAG, "hasSystemAlertWindowPermission(" + state.mRealCallingUid + ", "
+ + state.mRealCallingPid + ", " + state.mRealCallingPackage + ") "
+ + balStartModeToString(
+ state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
+ if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+ && mService.hasSystemAlertWindowPermission(state.mRealCallingUid,
+ state.mRealCallingPid, state.mRealCallingPackage)) {
+ Slog.w(
+ TAG,
+ "Background activity start for "
+ + state.mRealCallingPackage
+ + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+ return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
+ /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
+ }
+
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 3dc035e62e58..cbe3d79f10fc 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -401,6 +401,7 @@ class DeferredDisplayUpdater {
|| !Objects.equals(first.deviceProductInfo, second.deviceProductInfo)
|| first.modeId != second.modeId
|| first.renderFrameRate != second.renderFrameRate
+ || first.hasArrSupport != second.hasArrSupport
|| first.defaultModeId != second.defaultModeId
|| first.userPreferredModeId != second.userPreferredModeId
|| !Arrays.equals(first.supportedModes, second.supportedModes)
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 98919d9fd617..a4e4deb9ed7d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -503,6 +503,11 @@ class Task extends TaskFragment {
boolean mIsTrimmableFromRecents;
/**
+ * Sets whether the launch-adjacent flag is respected or not for this task or its child tasks.
+ */
+ private boolean mLaunchAdjacentDisabled;
+
+ /**
* Bounds offset should be applied when calculating compatible configuration for apps targeting
* SDK level 34 or before.
*/
@@ -3802,6 +3807,9 @@ class Task extends TaskFragment {
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
+ if (mLaunchAdjacentDisabled) {
+ pw.println(prefix + "mLaunchAdjacentDisabled=true");
+ }
}
@Override
@@ -5734,6 +5742,12 @@ class Task extends TaskFragment {
}
private boolean canMoveTaskToBack(Task task) {
+ // Checks whether a task is a child of this task because it can be reparetned when
+ // transition is deferred.
+ if (task != this && task.getParent() != this) {
+ return false;
+ }
+
// In LockTask mode, moving a locked task to the back of the root task may expose unlocked
// ones. Therefore we need to check if this operation is allowed.
if (!mAtmService.getLockTaskController().canMoveTaskToBack(task)) {
@@ -5803,7 +5817,7 @@ class Task extends TaskFragment {
(deferred) -> {
// Need to check again if deferred since the system might
// be in a different state.
- if (!isAttached() || (deferred && !canMoveTaskToBack(tr))) {
+ if (!tr.isAttached() || (deferred && !canMoveTaskToBack(tr))) {
Slog.e(TAG, "Failed to move task to back after saying we could: "
+ tr.mTaskId);
transition.abort();
@@ -6268,6 +6282,28 @@ class Task extends TaskFragment {
}
/**
+ * Sets this task and its children to disable respecting launch-adjacent.
+ */
+ void setLaunchAdjacentDisabled(boolean disabled) {
+ mLaunchAdjacentDisabled = disabled;
+ }
+
+ /**
+ * Returns whether this task or any of its ancestors have disabled respecting the
+ * launch-adjacent flag.
+ */
+ boolean isLaunchAdjacentDisabled() {
+ Task t = this;
+ while (t != null) {
+ if (t.mLaunchAdjacentDisabled) {
+ return true;
+ }
+ t = t.getParent().asTask();
+ }
+ return false;
+ }
+
+ /**
* Return true if the activityInfo has the same requiredDisplayCategory as this task.
*/
boolean isSameRequiredDisplayCategory(@NonNull ActivityInfo info) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 5dd3bbce4e96..2c71c1a1f4f3 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1074,6 +1074,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
// We only allow this for created by organizer tasks.
if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) {
+ Slog.i(TAG_WM, "Using launch root task from activity options: taskId="
+ + launchRootTask.mTaskId);
return launchRootTask;
}
}
@@ -1081,19 +1083,25 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Use launch-adjacent-flag-root if launching with launch-adjacent flag.
if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
&& mLaunchAdjacentFlagRootTask != null) {
+ final Task launchAdjacentRootAdjacentTask =
+ mLaunchAdjacentFlagRootTask.getAdjacentTask();
if (sourceTask != null && (sourceTask == candidateTask
|| sourceTask.topRunningActivity() == null)) {
// Do nothing when task that is getting opened is same as the source or when
// the source is no-longer valid.
Slog.w(TAG_WM, "Ignoring LAUNCH_ADJACENT because adjacent source is gone.");
} else if (sourceTask != null
- && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null
+ && launchAdjacentRootAdjacentTask != null
&& (sourceTask == mLaunchAdjacentFlagRootTask
|| sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
- // If the adjacent launch is coming from the same root, launch to
- // adjacent root instead.
- return mLaunchAdjacentFlagRootTask.getAdjacentTask();
+ // If the adjacent launch is coming from the same root that was specified as the
+ // launch-adjacent task, so instead we launch to its adjacent root instead.
+ Slog.i(TAG_WM, "Using adjacent-to specified launch-adjacent task: taskId="
+ + launchAdjacentRootAdjacentTask.mTaskId);
+ return launchAdjacentRootAdjacentTask;
} else {
+ Slog.i(TAG_WM, "Using specified launch-adjacent task: taskId="
+ + mLaunchAdjacentFlagRootTask.mTaskId);
return mLaunchAdjacentFlagRootTask;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 82c7a9350eca..166d74b132bd 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -69,6 +69,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
@@ -1450,6 +1451,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.setTrimmableFromRecents(hop.isTrimmableFromRecents());
break;
}
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: {
+ final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+ final Task task = container != null ? container.asTask() : null;
+ if (task == null || !task.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + container);
+ break;
+ }
+ task.setLaunchAdjacentDisabled(hop.isLaunchAdjacentDisabled());
+ break;
+ }
case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: {
if (mService.mBackNavigationController.restoreBackNavigation()) {
effects |= TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7e450dd965d6..aca6f7235714 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16815,6 +16815,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
EnforcingAdmin enforcingAdmin;
+
+ // TODO(b/370472975): enable when we stop policy enforecer callback from blocking the main
+ // thread
if (Flags.setPermissionGrantStateCoexistence()) {
enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
@@ -16840,6 +16843,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
callback.sendResult(null);
return;
}
+
+ // TODO(b/266924257): decide how to handle the internal state if the package doesn't
+ // exist, or the permission isn't requested by the app, because we could end up with
+ // inconsistent state between the policy engine and package manager. Also a package
+ // might get removed or has it's permission updated after we've set the policy.
+ if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ new IntegerPolicyValue(grantState),
+ caller.getUserId());
+ }
+ int newState = mInjector.binderWithCleanCallingIdentity(() ->
+ getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId()));
+ if (newState == grantState) {
+ callback.sendResult(Bundle.EMPTY);
+ } else {
+ callback.sendResult(null);
+ }
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
@@ -16862,9 +16890,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
+ boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+ >= android.os.Build.VERSION_CODES.Q;
+
try {
- boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
if (!isPostQAdmin) {
// Legacy admins assume that they cannot control pre-M apps
if (getTargetSdk(packageName, caller.getUserId())
@@ -16877,47 +16906,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
callback.sendResult(null);
return;
}
- } catch (SecurityException e) {
- Slogf.e(LOG_TAG, "Could not set permission grant state", e);
- callback.sendResult(null);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
- }
- }
- // TODO(b/278710449): enable when we stop policy enforecer callback from blocking the main
- // thread
- if (false) {
- // TODO(b/266924257): decide how to handle the internal state if the package doesn't
- // exist, or the permission isn't requested by the app, because we could end up with
- // inconsistent state between the policy engine and package manager. Also a package
- // might get removed or has it's permission updated after we've set the policy.
- if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- caller.getUserId());
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- new IntegerPolicyValue(grantState),
- caller.getUserId());
- }
- int newState = mInjector.binderWithCleanCallingIdentity(() ->
- getPermissionGrantStateForUser(
- packageName, permission, caller, caller.getUserId()));
- if (newState == grantState) {
- callback.sendResult(Bundle.EMPTY);
- } else {
- callback.sendResult(null);
- }
- } else {
- synchronized (getLockObject()) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
if (grantState == PERMISSION_GRANT_STATE_GRANTED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
@@ -16939,7 +16927,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
} catch (SecurityException e) {
Slogf.e(LOG_TAG, "Could not set permission grant state", e);
-
callback.sendResult(null);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01b2d3e34bdc..fdf6b809fa85 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2343,6 +2343,41 @@ public final class DisplayPowerControllerTest {
}
}
+ @Test
+ public void stylusUsageStarted_disablesAutomaticBrightnessStrategy() {
+ when(mDisplayManagerFlagsMock.isBlockAutobrightnessChangesOnStylusUsage())
+ .thenReturn(true);
+ when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled())
+ .thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ advanceTime(2);
+ clearInvocations(mHolder.automaticBrightnessController);
+ mHolder.dpc.stylusGestureStarted(2000000);
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+ verify(mHolder.automaticBrightnessController, times(0))
+ .getAutomaticScreenBrightness(any());
+
+ // Stylus usage timed out, hence autobrightness is now enabled back again
+ advanceTime(6);
+ verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(null);
+
+ // Ideally we should be able to assert against new BrightnessEvent(Display.DEFAULT_DISPLAY),
+ // but because brightnessEvent has the mTime field which refers to the current time,
+ // asserting against that is non-trivial
+ verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(
+ any(BrightnessEvent.class));
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -2406,6 +2441,7 @@ public final class DisplayPowerControllerTest {
.thenReturn(new int[0]);
when(displayDeviceConfigMock.getDefaultDozeBrightness())
.thenReturn(DEFAULT_DOZE_BRIGHTNESS);
+ when(displayDeviceConfigMock.getIdleStylusTimeoutMillis()).thenReturn(5);
when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
.thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index d831cf8a3643..b6da3ae6a5cd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -51,6 +51,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -209,8 +210,8 @@ public class LogicalDisplayMapperTest {
when(mResourcesMock.getIntArray(
com.android.internal.R.array.config_deviceStatesOnWhichToSleep))
.thenReturn(new int[]{0});
- when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any())).thenAnswer(
- AdditionalAnswers.returnsSecondArg());
+ when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any(), anyBoolean()))
+ .thenAnswer(AdditionalAnswers.returnsSecondArg());
when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(false);
mLooper = new TestLooper();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 8936f061963c..b002a1f73006 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -87,7 +88,7 @@ public class LogicalDisplayTest {
mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)};
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
- when(mSyntheticModeManager.createAppSupportedModes(any(), any())).thenAnswer(
+ when(mSyntheticModeManager.createAppSupportedModes(any(), any(), anyBoolean())).thenAnswer(
AdditionalAnswers.returnsSecondArg());
// Disable binder caches in this process.
@@ -582,7 +583,8 @@ public class LogicalDisplayTest {
Display.Mode[] appSupportedModes = new Display.Mode[] {new Display.Mode(OTHER_MODE_ID,
DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 45)};
when(mSyntheticModeManager.createAppSupportedModes(
- any(), eq(mDisplayDeviceInfo.supportedModes))).thenReturn(appSupportedModes);
+ any(), eq(mDisplayDeviceInfo.supportedModes), anyBoolean()))
+ .thenReturn(appSupportedModes);
mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager);
DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index 04b79b4f1761..d93ee84f4870 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -74,7 +74,7 @@ public final class BrightnessReasonTest {
@Test
public void setModifierDoesntSetIfModifierIsBeyondExtremes() {
- int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2
+ int extremeModifier = 0x80;
// reset modifier
mBrightnessReason.setModifier(0);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c0698756a3d7..b3baa5deb4a7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -125,7 +125,7 @@ public final class DisplayBrightnessControllerTest {
DisplayManagerInternal.DisplayOffloadSession.class));
verify(displayBrightnessStrategy).updateBrightness(
eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
- /* userSetBrightnessChanged= */ false)));
+ /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false)));
assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
displayBrightnessStrategy);
}
@@ -559,4 +559,11 @@ public final class DisplayBrightnessControllerTest {
displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled,
leadDisplayId);
}
+
+ @Test
+ public void setStylusBeingUsed_setsStylusInUseState() {
+ assertFalse(mDisplayBrightnessController.isStylusBeingUsed());
+ mDisplayBrightnessController.setStylusBeingUsed(true);
+ assertTrue(mDisplayBrightnessController.isStylusBeingUsed());
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index a44c517ed9cf..fe1505162e24 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class DisplayBrightnessStrategySelectorTest {
private static final boolean DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING = false;
+ private static final boolean STYLUS_IS_NOT_BEING_USED = false;
+ private static final boolean STYLUS_IS_BEING_USED = true;
private static final int DISPLAY_ID = 1;
@Mock
@@ -196,7 +198,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -212,7 +215,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -226,7 +230,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -249,7 +254,8 @@ public final class DisplayBrightnessStrategySelectorTest {
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -259,7 +265,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DisplayManagerInternal.DisplayPowerRequest.class);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mScreenOffBrightnessModeStrategy);
}
@@ -271,7 +278,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mOverrideBrightnessStrategy);
}
@@ -284,7 +292,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mTemporaryBrightnessStrategy);
}
@@ -298,7 +307,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mBoostBrightnessStrategy);
}
@@ -312,7 +322,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mInvalidBrightnessStrategy);
}
@@ -323,7 +334,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mFollowerBrightnessStrategy);
}
@@ -341,7 +353,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mOffloadBrightnessStrategy);
}
@@ -365,7 +378,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mAutomaticBrightnessStrategy);
verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON,
true, BrightnessReason.REASON_UNKNOWN,
@@ -373,6 +387,32 @@ public final class DisplayBrightnessStrategySelectorTest {
/* useNormalBrightnessForDoze= */ false, 0.1f, false);
}
+
+ @Test
+ public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() {
+ when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+ true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true);
+ when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
+ assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+ new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_BEING_USED)),
+ mAutomaticBrightnessStrategy);
+ }
+
@Test
public void selectStrategy_selectsAutomaticFallbackStrategyWhenValid() {
when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
@@ -389,7 +429,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mAutoBrightnessFallbackStrategy);
}
@@ -407,7 +448,8 @@ public final class DisplayBrightnessStrategySelectorTest {
assertNotEquals(mOffloadBrightnessStrategy,
mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)));
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)));
}
@Test
@@ -425,7 +467,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mFallbackBrightnessStrategy);
}
@@ -440,7 +483,8 @@ public final class DisplayBrightnessStrategySelectorTest {
mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession));
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED));
StrategySelectionNotifyRequest strategySelectionNotifyRequest =
new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 99dfa739fb80..2a71af06e0c2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -129,7 +129,8 @@ public class AutoBrightnessFallbackStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mAutoBrightnessFallbackStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index efa8b3ef775f..8a1f86093ecf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -637,7 +637,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -686,7 +686,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -725,7 +725,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -764,7 +764,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 275bb3efee8e..c03309e8b4a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class BoostBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mBoostBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 23e447c25245..e7f80b04e669 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@ public class DozeBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mDozeBrightnessModeStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4a579092d38..dcfa174a53f5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -61,7 +61,8 @@ public class FallbackBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mFallbackBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index c01f96e800de..239cdb6002e9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@ public class FollowerBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mFollowerBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index 9fb2afa26ed2..77302f8747c1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@ public class OffloadBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mOffloadBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
.getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index e8b4c06b9c89..cc21af19722b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class OverrideBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mOverrideBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index 38709ece7007..652663e52a0a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@ public final class ScreenOffBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mScreenOffBrightnessModeStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index f523b6af426b..0022cab371e8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class TemporaryBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mTemporaryBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
index b2d83d744ce6..9a93fba040cc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
@@ -43,35 +43,42 @@ class SyntheticModeManagerTest {
@Test
fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) {
whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled)
+ whenever(mockFlags.hasArrSupportFlag()).thenReturn(testCase.hasArrSupport)
whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
val syntheticModeManager = SyntheticModeManager(mockFlags)
val result = syntheticModeManager.createAppSupportedModes(
- mockConfig, testCase.supportedModes)
+ mockConfig, testCase.supportedModes, testCase.hasArrSupport)
assertThat(result).isEqualTo(testCase.expectedAppModes)
}
+ // TODO(b/361433651) Remove vrrSupported once hasArrSupport is rolled out
enum class AppSupportedModesTestCase(
val syntheticModesEnabled: Boolean,
val vrrSupported: Boolean,
+ val hasArrSupport: Boolean,
val supportedModes: Array<Mode>,
val expectedAppModes: Array<Mode>
) {
- SYNTHETIC_MODES_NOT_SUPPORTED(false, true, DISPLAY_MODES, DISPLAY_MODES),
- VRR_NOT_SUPPORTED(true, false, DISPLAY_MODES, DISPLAY_MODES),
- VRR_SYNTHETIC_NOT_SUPPORTED(false, false, DISPLAY_MODES, DISPLAY_MODES),
- SINGLE_RESOLUTION_MODES(true, true, DISPLAY_MODES, arrayOf(
+ SYNTHETIC_MODES_NOT_SUPPORTED(false, true, true, DISPLAY_MODES, DISPLAY_MODES),
+ VRR_NOT_SUPPORTED(true, false, false, DISPLAY_MODES, DISPLAY_MODES),
+ VRR_SYNTHETIC_NOT_SUPPORTED(false, false, false, DISPLAY_MODES, DISPLAY_MODES),
+ SINGLE_RESOLUTION_MODES(true, true, true, DISPLAY_MODES, arrayOf(
Mode(2, 100, 100, 120f),
Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
)),
- NO_60HZ_MODES(true, true, arrayOf(Mode(2, 100, 100, 120f)),
+ SINGLE_RESOLUTION_MODES_HASARR(true, false, true, DISPLAY_MODES, arrayOf(
+ Mode(2, 100, 100, 120f),
+ Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
+ )),
+ NO_60HZ_MODES(true, true, true, arrayOf(Mode(2, 100, 100, 120f)),
arrayOf(
Mode(2, 100, 100, 120f),
Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
)
),
- MULTI_RESOLUTION_MODES(true, true,
+ MULTI_RESOLUTION_MODES(true, true, true,
arrayOf(
Mode(1, 100, 100, 120f),
Mode(2, 200, 200, 60f),
@@ -86,7 +93,7 @@ class SyntheticModeManagerTest {
Mode(7, 300, 300, 60f, 60f, true, floatArrayOf(), intArrayOf())
)
),
- WITH_HDR_TYPES(true, true,
+ WITH_HDR_TYPES(true, true, true,
arrayOf(
Mode(1, 100, 100, 120f, 120f, false, floatArrayOf(), intArrayOf(1, 2)),
Mode(2, 200, 200, 60f, 120f, false, floatArrayOf(), intArrayOf(3, 4)),
@@ -99,7 +106,7 @@ class SyntheticModeManagerTest {
Mode(5, 200, 200, 60f, 60f, true, floatArrayOf(), intArrayOf(5, 6)),
)
),
- UNACHIEVABLE_60HZ(true, true,
+ UNACHIEVABLE_60HZ(true, true, true,
arrayOf(
Mode(1, 100, 100, 90f),
),
@@ -107,7 +114,7 @@ class SyntheticModeManagerTest {
Mode(1, 100, 100, 90f),
)
),
- MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true,
+ MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true, true,
arrayOf(
Mode(1, 100, 100, 120f),
Mode(2, 200, 200, 90f),
@@ -118,7 +125,7 @@ class SyntheticModeManagerTest {
Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()),
)
),
- LOWER_THAN_60HZ_MODES(true, true,
+ LOWER_THAN_60HZ_MODES(true, true, true,
arrayOf(
Mode(1, 100, 100, 30f),
Mode(2, 100, 100, 45f),
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 121145672d68..439243e85e75 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -82,6 +82,11 @@ public class AudioManagerRouteControllerTest {
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET =
createAudioDeviceInfo(
AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null);
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ "name_wired_hs_with_address",
+ /* address= */ "card=1;device=0");
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP =
createAudioDeviceInfo(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45");
@@ -304,6 +309,55 @@ public class AudioManagerRouteControllerTest {
assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME);
}
+ @Test
+ public void getAvailableRoutes_whenAddressIsPopulatedForNonBluetoothDevice_usesCorrectName() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+
+ List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+ assertThat(availableRoutes.size()).isEqualTo(3);
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+ .getName()
+ .toString())
+ .isEqualTo(
+ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS
+ .getProductName()
+ .toString());
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP.getProductName().toString());
+ }
+
+ @Test
+ public void
+ getAvailableRoutes_whenAddressIsNotPopulatedForNonBluetoothDevice_usesCorrectName() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+
+ List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+ assertThat(availableRoutes.size()).isEqualTo(2);
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString());
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET.getProductName().toString());
+ }
+
// Internal methods.
@NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 9ba272446689..625dbe6e16f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.media.AudioAttributes.USAGE_ALARM;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -91,6 +92,7 @@ public class BackgroundUserSoundNotifierTest {
}
@Test
public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException {
+ assumeTrue(UserManager.supportsMultipleUsers());
AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
final int fgUserId = mSpiedContext.getUserId();
@@ -100,7 +102,7 @@ public class BackgroundUserSoundNotifierTest {
/* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verify(mNotificationManager)
.notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
eq(afi.getClientUid()), any(Notification.class),
@@ -116,7 +118,7 @@ public class BackgroundUserSoundNotifierTest {
/* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verifyZeroInteractions(mNotificationManager);
}
@@ -131,7 +133,7 @@ public class BackgroundUserSoundNotifierTest {
AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verifyZeroInteractions(mNotificationManager);
}
@@ -169,7 +171,7 @@ public class BackgroundUserSoundNotifierTest {
doReturn(focusStack).when(mockAudioPolicy).getFocusStack();
mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy;
- mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext);
+ mBackgroundUserSoundNotifier.muteAlarmSounds(bgUserUid);
verify(apc1.getPlayerProxy()).stop();
verify(mockAudioPolicy).sendFocusLossAndUpdate(afi);
@@ -178,6 +180,7 @@ public class BackgroundUserSoundNotifierTest {
@Test
public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() {
+ assumeTrue(UserManager.supportsMultipleUsers());
final int fgUserId = mSpiedContext.getUserId();
UserInfo bgUser = createUser("Background User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
int bgUserUid = bgUser.id * 100000;
@@ -205,7 +208,7 @@ public class BackgroundUserSoundNotifierTest {
.when(mUserManager).getUserSwitchability(any());
Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
- mSpiedContext);
+ mSpiedContext, 101000);
assertEquals("Alarm for BgUser", notification.extras.getString(
Notification.EXTRA_TITLE));
@@ -232,7 +235,7 @@ public class BackgroundUserSoundNotifierTest {
.when(mUserManager).getUserSwitchability(any());
Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
- mSpiedContext);
+ mSpiedContext, 101000);
assertEquals(1, notification.actions.length);
assertEquals(mSpiedContext.getString(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 39acd8dd816a..43a8aa957fa5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -72,7 +72,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
@@ -486,7 +485,7 @@ public class StagingManagerTest {
FakeStagedSession session = new FakeStagedSession(239);
session.setIsApex(true);
// Call and verify
- Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ var result = mStagingManager.getStagedApexInfos(session);
assertThat(result).isEmpty();
}
// Invalid session: destroyed
@@ -496,7 +495,7 @@ public class StagingManagerTest {
session.setIsApex(true);
session.setDestroyed(true);
// Call and verify
- Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ var result = mStagingManager.getStagedApexInfos(session);
assertThat(result).isEmpty();
}
}
@@ -520,8 +519,8 @@ public class StagingManagerTest {
when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
// Call and verify
- Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
- assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]);
+ List<ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
+ assertThat(result).containsExactly(fakeApexInfos[0]);
ArgumentCaptor<ApexSessionParams> argumentCaptor =
ArgumentCaptor.forClass(ApexSessionParams.class);
@@ -544,9 +543,8 @@ public class StagingManagerTest {
when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
// Call and verify
- Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
- assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0],
- fakeApexInfos[1].moduleName, fakeApexInfos[1]);
+ List<ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
+ assertThat(result).containsExactly(fakeApexInfos[0], fakeApexInfos[1]);
ArgumentCaptor<ApexSessionParams> argumentCaptor =
ArgumentCaptor.forClass(ApexSessionParams.class);
@@ -557,7 +555,7 @@ public class StagingManagerTest {
}
@Test
- public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception {
+ public void getStagedApexInfos_returnsStagedApexModules() throws Exception {
FakeStagedSession validSession1 = new FakeStagedSession(239);
validSession1.setIsApex(true);
validSession1.setSessionReady();
@@ -575,8 +573,8 @@ public class StagingManagerTest {
mockApexManagerGetStagedApexInfoWithSessionId();
- List<String> result = mStagingManager.getStagedApexModuleNames();
- assertThat(result).containsExactly("239", "123", "124");
+ List<StagedApexInfo> result = mStagingManager.getStagedApexInfos();
+ assertThat(result).containsExactly((Object[]) fakeStagedApexInfos("239", "123", "124"));
verify(mApexManager, times(2)).getStagedApexInfos(any());
}
@@ -605,26 +603,12 @@ public class StagingManagerTest {
});
}
- @Test
- public void getStagedApexInfo() throws Exception {
- FakeStagedSession validSession1 = new FakeStagedSession(239);
- validSession1.setIsApex(true);
- validSession1.setSessionReady();
- mStagingManager.createSession(validSession1);
- ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
- when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
-
- // Verify null is returned if module name is not found
- StagedApexInfo result = mStagingManager.getStagedApexInfo("not found");
- assertThat(result).isNull();
- verify(mApexManager, times(1)).getStagedApexInfos(any());
- // Otherwise, the correct object is returned
- result = mStagingManager.getStagedApexInfo("module1");
- assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName);
- assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath);
- assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode);
- assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName);
- verify(mApexManager, times(2)).getStagedApexInfos(any());
+ private StagedApexInfo[] fakeStagedApexInfos(String... moduleNames) {
+ return Arrays.stream(moduleNames).map(moduleName -> {
+ StagedApexInfo info = new StagedApexInfo();
+ info.moduleName = moduleName;
+ return info;
+ }).toArray(StagedApexInfo[]::new);
}
@Test
@@ -646,8 +630,8 @@ public class StagingManagerTest {
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
ApexStagedEvent.class);
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
- assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
- new String[]{"239"});
+ assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo(
+ fakeStagedApexInfos("239"));
}
// Create another staged session and verify observers are notified of union
@@ -662,8 +646,8 @@ public class StagingManagerTest {
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
ApexStagedEvent.class);
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
- assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
- new String[]{"239", "240"});
+ assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo(
+ fakeStagedApexInfos("239", "240"));
}
// Finally, verify that once unregistered, observer is not notified
@@ -699,7 +683,7 @@ public class StagingManagerTest {
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
ApexStagedEvent.class);
verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
- assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
+ assertThat(argumentCaptor.getValue().stagedApexInfos).hasLength(0);
}
@Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index a1db18232c09..1c7fc63efd41 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -54,6 +54,7 @@ import android.os.test.TestLooper;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.IntArray;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayAddress;
@@ -384,6 +385,32 @@ public class NotifierTest {
}
@Test
+ public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() {
+ createNotifier();
+ // GIVEN per-display wake by touch is enabled and one display group has been defined
+ when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true);
+ final int groupId = 313;
+ final int displayId1 = 3113;
+ final int displayId2 = 4114;
+ final int[] displays = new int[]{displayId1, displayId2};
+ when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays));
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays);
+ mNotifier.onGroupWakefulnessChangeStarted(
+ groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000);
+ final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray();
+ expectedDisplayInteractivities.put(displayId1, true);
+ expectedDisplayInteractivities.put(displayId2, true);
+ verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities);
+
+ // WHEN display group is removed
+ when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>());
+ mNotifier.onGroupRemoved(groupId);
+
+ // THEN native input manager is informed that displays in that group no longer exist
+ verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray());
+ }
+
+ @Test
public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
diff --git a/services/tests/security/forensic/OWNERS b/services/tests/security/forensic/OWNERS
new file mode 100644
index 000000000000..80c9afb96033
--- /dev/null
+++ b/services/tests/security/forensic/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 3e2949d60183..de5564cb7704 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -20,6 +20,8 @@ import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
import static com.android.media.audio.Flags.absVolumeIndexFix;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -109,12 +111,13 @@ public class AudioDeviceVolumeManagerTest {
mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ eq(AudioManager.STREAM_MUSIC), eq(minIndex), anyBoolean(),
+ eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ AudioManager.STREAM_MUSIC, midIndex, false, AudioSystem.DEVICE_OUT_USB_DEVICE);
}
@Test
@@ -151,7 +154,7 @@ public class AudioDeviceVolumeManagerTest {
// Stream volume changes
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, targetIndex,
+ AudioManager.STREAM_MUSIC, targetIndex, false,
AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
@@ -162,7 +165,7 @@ public class AudioDeviceVolumeManagerTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, maxIndex,
+ AudioManager.STREAM_MUSIC, maxIndex, false,
AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
@@ -193,8 +196,8 @@ public class AudioDeviceVolumeManagerTest {
}
// Stream volume changes
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, passedIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ AudioManager.STREAM_MUSIC, passedIndex, false,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
// Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -207,7 +210,7 @@ public class AudioDeviceVolumeManagerTest {
passedIndex = 4;
}
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- AudioManager.STREAM_MUSIC, passedIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ AudioManager.STREAM_MUSIC, passedIndex, false,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 96ac5d251ffd..ce59a86c6ca3 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -132,12 +132,13 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
}
@Override
- public int setStreamVolumeIndexAS(int stream, int index, int device) {
+ public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override
- public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted,
+ int device) {
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
index dc8c1b9c8a10..6b41c434b80f 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -18,6 +18,7 @@ package com.android.server.audio;
import static android.media.AudioManager.ADJUST_LOWER;
import static android.media.AudioManager.ADJUST_MUTE;
import static android.media.AudioManager.ADJUST_RAISE;
+import static android.media.AudioManager.ADJUST_UNMUTE;
import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER;
import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
import static android.media.AudioManager.DEVICE_OUT_SPEAKER;
@@ -41,13 +42,13 @@ import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+import static com.android.media.audio.Flags.FLAG_RING_MY_CAR;
import static com.android.media.audio.Flags.absVolumeIndexFix;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
@@ -180,6 +181,10 @@ public class VolumeHelperTest {
}
return mStreamDevice.get(stream);
}
+
+ public void setMuteAffectedStreams(int muteAffectedStreams) {
+ mMuteAffectedStreams = muteAffectedStreams;
+ }
}
private static class TestDeviceVolumeBehaviorDispatcherStub
@@ -223,6 +228,7 @@ public class VolumeHelperTest {
mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer,
mMockPermissionProvider);
+ mAudioService.setMuteAffectedStreams(AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED);
mTestLooper.dispatchAll();
prepareAudioServiceState();
@@ -258,6 +264,8 @@ public class VolumeHelperTest {
for (int streamType : usedStreamTypes) {
mAudioService.setStreamVolume(streamType, DEFAULT_STREAM_VOLUME, /*flags=*/0,
mContext.getOpPackageName());
+ mAudioService.adjustStreamVolume(streamType, ADJUST_UNMUTE, /*flags=*/0,
+ mContext.getOpPackageName());
}
if (!mIsAutomotive) {
@@ -301,7 +309,20 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE));
+ STREAM_MUSIC, newIndex, /*muted=*/false, DEVICE_OUT_USB_DEVICE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_RING_MY_CAR)
+ public void adjustStreamVolume_adjustMute_callsASSetStreamVolumeIndex() throws Exception {
+ int currentIndex = mAudioService.getStreamVolume(STREAM_MUSIC);
+
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_MUTE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), eq(currentIndex), /*muted=*/eq(true), anyInt());
}
@Test
@@ -325,7 +346,7 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE));
}
@Test
@@ -341,7 +362,7 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_MUSIC), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
}
// --------------- Volume Group APIs ---------------
@@ -356,15 +377,15 @@ public class VolumeHelperTest {
mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
circularNoMinMaxIncrementVolume(STREAM_MUSIC), /*flags=*/0,
- mContext.getOpPackageName(), /*attributionTag*/null);
+ mContext.getOpPackageName(), /*attributionTag*/null);
mTestLooper.dispatchAll();
- verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(),
+ verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(), eq(false),
eq(DEVICE_OUT_USB_DEVICE));
}
@Test
- public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception {
+ public void adjustVolumeGroupVolume_callsASSetStreamVolumeIndexAS() throws Exception {
assumeNotNull(mAudioMusicVolumeGroup);
mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
@@ -372,8 +393,24 @@ public class VolumeHelperTest {
ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName());
mTestLooper.dispatchAll();
- verify(mSpyAudioSystem).setVolumeIndexForAttributes(
- any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ anyInt(), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_RING_MY_CAR)
+ public void adjustVolumeGroupVolume_adjustMute_callsASSetStreamVolumeIndexAS()
+ throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(),
+ ADJUST_MUTE, /*flags=*/0, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ anyInt(), anyInt(), eq(true), anyInt());
}
@Test
@@ -437,7 +474,7 @@ public class VolumeHelperTest {
@Test
public void check_isStreamAffectedByMute() {
- assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
+ assertTrue(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
}
// --------------------- Volume Flag Check --------------------
@@ -452,14 +489,14 @@ public class VolumeHelperTest {
mContext.getOpPackageName());
mTestLooper.dispatchAll();
verify(mSpyAudioSystem).setStreamVolumeIndexAS(
- eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
reset(mSpyAudioSystem);
mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
mTestLooper.dispatchAll();
verify(mSpyAudioSystem).setStreamVolumeIndexAS(
- eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
}
@Test
@@ -471,13 +508,13 @@ public class VolumeHelperTest {
mContext.getOpPackageName());
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
- eq(STREAM_NOTIFICATION), eq(newIndex), eq(DEVICE_OUT_BLE_SPEAKER));
+ eq(STREAM_NOTIFICATION), eq(newIndex), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
- eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
}
@Test
@@ -523,7 +560,7 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), anyInt(), anyInt());
+ eq(STREAM_MUSIC), anyInt(), eq(false), anyInt());
}
@Test
@@ -537,7 +574,7 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
- eq(STREAM_VOICE_CALL), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_VOICE_CALL), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
mAudioService.setDeviceForStream(STREAM_BLUETOOTH_SCO, DEVICE_OUT_BLUETOOTH_SCO);
mAudioService.adjustStreamVolume(STREAM_BLUETOOTH_SCO, ADJUST_MUTE, /*flags=*/0,
@@ -545,7 +582,7 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
- eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
}
// ----------------- AudioDeviceVolumeManager -----------------
@@ -568,18 +605,18 @@ public class VolumeHelperTest {
mTestLooper.dispatchAll();
// there is a min/max index mismatch in automotive
- assertEquals(volMin, mAudioService.getDeviceVolume(volMin, usbDevice,
- mContext.getOpPackageName()));
+ assertEquals(volMin.getVolumeIndex(), mAudioService.getDeviceVolume(volMin, usbDevice,
+ mContext.getOpPackageName()).getVolumeIndex());
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
mTestLooper.dispatchAll();
// there is a min/max index mismatch in automotive
- assertEquals(volMid, mAudioService.getDeviceVolume(volMid, usbDevice,
- mContext.getOpPackageName()));
+ assertEquals(volMid.getVolumeIndex(), mAudioService.getDeviceVolume(volMid, usbDevice,
+ mContext.getOpPackageName()).getVolumeIndex());
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
+ eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
}
@Test
@@ -617,8 +654,7 @@ public class VolumeHelperTest {
mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()));
// Stream volume changes
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- STREAM_MUSIC, targetIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ STREAM_MUSIC, targetIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
// Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -630,8 +666,7 @@ public class VolumeHelperTest {
assertEquals(volIndex4,
mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()));
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- STREAM_MUSIC, maxIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ STREAM_MUSIC, maxIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
@Test
@@ -660,8 +695,7 @@ public class VolumeHelperTest {
}
// Stream volume changes
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- STREAM_MUSIC, passedIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
// Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -674,8 +708,7 @@ public class VolumeHelperTest {
passedIndex = 4;
}
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
- STREAM_MUSIC, passedIndex,
- AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
// ---------------- DeviceVolumeBehaviorTest ----------------
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index eb9cce007b77..d1f6c2f9f1f0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -69,7 +69,6 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.compat.PlatformCompat;
-import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.testutils.TestUtils;
@@ -138,7 +137,6 @@ public class AppIntegrityManagerServiceImplTest {
@Mock PlatformCompat mPlatformCompat;
@Mock Context mMockContext;
@Mock Resources mMockResources;
- @Mock RuleEvaluationEngine mRuleEvaluationEngine;
@Mock IntegrityFileManager mIntegrityFileManager;
@Mock Handler mHandler;
@@ -176,7 +174,6 @@ public class AppIntegrityManagerServiceImplTest {
mMockContext,
mPackageManagerInternal,
mParserSupplier,
- mRuleEvaluationEngine,
mIntegrityFileManager,
mHandler);
@@ -307,91 +304,6 @@ public class AppIntegrityManagerServiceImplTest {
}
@Test
- public void handleBroadcast_correctArgs() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- Intent intent = makeVerificationIntent();
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
- runJobInHandler();
-
- ArgumentCaptor<AppInstallMetadata> metadataCaptor =
- ArgumentCaptor.forClass(AppInstallMetadata.class);
- verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
- AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
- assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
- assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
- assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
- // we cannot check installer cert because it seems to be device specific.
- assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
- assertFalse(appInstallMetadata.isPreInstalled());
- // Asserting source stamp not present.
- assertFalse(appInstallMetadata.isStampPresent());
- assertFalse(appInstallMetadata.isStampVerified());
- assertFalse(appInstallMetadata.isStampTrusted());
- assertNull(appInstallMetadata.getStampCertificateHash());
- // These are hardcoded in the test apk android manifest
- Map<String, String> allowedInstallers =
- appInstallMetadata.getAllowedInstallersAndCertificates();
- assertEquals(2, allowedInstallers.size());
- assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG));
- assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER));
- }
-
- @Test
- public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- Intent intent = makeVerificationIntent();
- intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE);
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
- runJobInHandler();
-
- ArgumentCaptor<AppInstallMetadata> metadataCaptor =
- ArgumentCaptor.forClass(AppInstallMetadata.class);
- verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
- AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
- assertThat(appInstallMetadata.getAppCertificates())
- .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
- }
-
- @Test
- public void handleBroadcast_correctArgs_sourceStamp() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- Intent intent = makeVerificationIntent();
- intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE);
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
- runJobInHandler();
-
- ArgumentCaptor<AppInstallMetadata> metadataCaptor =
- ArgumentCaptor.forClass(AppInstallMetadata.class);
- verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
- AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
- assertTrue(appInstallMetadata.isStampPresent());
- assertTrue(appInstallMetadata.isStampVerified());
- assertTrue(appInstallMetadata.isStampTrusted());
- assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash());
- }
-
- @Test
public void handleBroadcast_allow() throws Exception {
allowlistUsAsRuleProvider();
makeUsSystemApp();
@@ -400,7 +312,6 @@ public class AppIntegrityManagerServiceImplTest {
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent();
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
@@ -411,32 +322,6 @@ public class AppIntegrityManagerServiceImplTest {
}
@Test
- public void handleBroadcast_reject() throws Exception {
- allowlistUsAsRuleProvider();
- makeUsSystemApp();
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- when(mRuleEvaluationEngine.evaluate(any()))
- .thenReturn(
- IntegrityCheckResult.deny(
- Arrays.asList(
- new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, false),
- Rule.DENY))));
- Intent intent = makeVerificationIntent();
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
- runJobInHandler();
-
- verify(mPackageManagerInternal)
- .setIntegrityVerificationResult(
- 1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
- }
-
- @Test
public void handleBroadcast_notInitialized() throws Exception {
allowlistUsAsRuleProvider();
makeUsSystemApp();
@@ -446,7 +331,6 @@ public class AppIntegrityManagerServiceImplTest {
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent();
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
@@ -467,8 +351,6 @@ public class AppIntegrityManagerServiceImplTest {
verify(mMockContext, atLeastOnce())
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
- when(mRuleEvaluationEngine.evaluate(any()))
- .thenReturn(IntegrityCheckResult.deny(/* rule= */ null));
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
deleted file mode 100644
index e1ee9c3a1c8f..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.IntegrityFileManager;
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(JUnit4.class)
-public class RuleEvaluationEngineTest {
-
- private static final String INSTALLER_1 = "installer1";
- private static final String INSTALLER_1_CERT = "installer1_cert";
- private static final String INSTALLER_2 = "installer2";
- private static final String INSTALLER_2_CERT = "installer2_cert";
-
- private static final String RANDOM_INSTALLER = "random";
- private static final String RANDOM_INSTALLER_CERT = "random_cert";
-
- @Mock
- private IntegrityFileManager mIntegrityFileManager;
-
- private RuleEvaluationEngine mEngine;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mEngine = new RuleEvaluationEngine(mIntegrityFileManager);
-
- when(mIntegrityFileManager.readRules(any())).thenReturn(Collections.singletonList(new Rule(
- IntegrityFormula.Installer.notAllowedByManifest(), Rule.DENY)));
-
- when(mIntegrityFileManager.initialized()).thenReturn(true);
- }
-
- @Test
- public void testAllowedInstallers_empty() {
- AppInstallMetadata appInstallMetadata1 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
- .build();
- AppInstallMetadata appInstallMetadata2 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
- .build();
- AppInstallMetadata appInstallMetadata3 =
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
- .build();
-
- assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- }
-
- @Test
- public void testAllowedInstallers_oneElement() {
- Map<String, String> allowedInstallers =
- Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);
-
- AppInstallMetadata appInstallMetadata1 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
- .setAllowedInstallersAndCert(allowedInstallers)
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata2 =
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata3 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata4 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- }
-
- @Test
- public void testAllowedInstallers_multipleElement() {
- Map<String, String> allowedInstallers = new HashMap<>(2);
- allowedInstallers.put(INSTALLER_1, INSTALLER_1_CERT);
- allowedInstallers.put(INSTALLER_2, INSTALLER_2_CERT);
-
- AppInstallMetadata appInstallMetadata1 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata2 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata3 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
- AppInstallMetadata appInstallMetadata4 =
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setAllowedInstallersAndCert(allowedInstallers)
- .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
- .build();
- assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
- .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- }
-
- /** Returns a builder with all fields filled with some placeholder data. */
- private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
- return new AppInstallMetadata.Builder()
- .setPackageName("abc")
- .setAppCertificates(Collections.singletonList("abc"))
- .setAppCertificateLineage(Collections.singletonList("abc"))
- .setInstallerCertificates(Collections.singletonList("abc"))
- .setInstallerName("abc")
- .setVersionCode(-1)
- .setIsPreInstalled(true);
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index c247c08c8010..3b0cb4ad8779 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -68,6 +68,7 @@ import static org.testng.Assert.assertThrows;
import android.Manifest;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.Flags;
import android.app.IOnProjectionStateChangedListener;
@@ -247,6 +248,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
mInjector = spy(new TestInjector());
mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
mTwilightManager, mInjector);
+ // Initialize the current user.
+ mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser());
try {
mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
} catch (SecurityException e) {/* ignore for permission denial */}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 50a5f658f059..4391152220c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -546,7 +546,8 @@ public class NotificationRecordTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+ @EnableFlags(com.android.server.notification.Flags
+ .FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI_FOR_CHANNEL)
public void testVibration_customVibrationForSound_withVibrationUri() throws IOException {
defaultChannel.enableVibration(true);
VibrationInfo vibration = getTestingVibration(mVibrator);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index d0080d29f82b..d5f86b6feac8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1261,6 +1261,33 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
+ public void testLaunchAdjacentDisabled() {
+ final ActivityStarter starter =
+ prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
+ final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ // Activity must not land on split-screen task if currently not in split-screen mode.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testLaunchAdjacentDisabled")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inMultiWindowMode()).isFalse();
+
+ // Move activity to split-screen-primary task and make sure it has the focus.
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
+ top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
+ top.getRootTask().moveToFront("testLaunchAdjacentDisabled");
+ top.getRootTask().setLaunchAdjacentDisabled(true);
+
+ // Ensure activity does not launch into split-screen-secondary when launch adjacent is
+ // disabled
+ startActivityInner(starter, outActivity[0], top, options, null /* inTask */,
+ null /* taskFragment*/);
+ assertThat(outActivity[0].isDescendantOf(splitOrg.mSecondary)).isFalse();
+ }
+
+ @Test
public void testTransientLaunchWithKeyguard() {
final ActivityStarter starter = prepareStarter(0 /* flags */);
final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 3910904337b2..750968100e2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -683,6 +683,41 @@ public class BackgroundActivityStartControllerExemptionTests {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testRealCaller_sawPermission() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ when(mService.hasSystemAlertWindowPermission(eq(realCallingUid), eq(realCallingPid),
+ any())).thenReturn(true);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions =
+ mCheckedOptions.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, null,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForCaller(callerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_SAW_PERMISSION);
+ }
+
+ @Test
public void testCaller_isRecents() {
int callingUid = REGULAR_UID_1;
int callingPid = REGULAR_PID_1;
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 72f4fa9158fb..c1edae91aabb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4883,7 +4883,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
- @EnableCompatChanges({ActivityRecord.UNIVERSAL_RESIZABLE_BY_DEFAULT})
+ @EnableCompatChanges({ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT})
public void testUniversalResizeableByDefault() {
mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
mDisplayContent.setIgnoreOrientationRequest(false);
diff --git a/services/usb/java/com/android/server/usb/UsbManagerInternal.java b/services/usb/java/com/android/server/usb/UsbManagerInternal.java
index c97df6b4f63a..31c5986c45b8 100644
--- a/services/usb/java/com/android/server/usb/UsbManagerInternal.java
+++ b/services/usb/java/com/android/server/usb/UsbManagerInternal.java
@@ -34,9 +34,11 @@ import java.lang.annotation.RetentionPolicy;
public abstract class UsbManagerInternal {
public static final int OS_USB_DISABLE_REASON_AAPM = 0;
+ public static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1;
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {OS_USB_DISABLE_REASON_AAPM})
+ @IntDef(value = {OS_USB_DISABLE_REASON_AAPM,
+ OS_USB_DISABLE_REASON_LOCKDOWN_MODE})
public @interface OsUsbDisableReason {
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index ba9dff656f0a..ec4f7e1ea4ba 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -1527,8 +1527,11 @@ public class UsbService extends IUsbManager.Stub {
}
mLockdownModeStatus = lockDownTriggeredByUser;
for (UsbPort port: mPortManager.getPorts()) {
- enableUsbData(port.getId(), !lockDownTriggeredByUser, STRONG_AUTH_OPERATION_ID,
- new IUsbOperationInternal.Default());
+ enableUsbDataInternal(port.getId(), !lockDownTriggeredByUser,
+ STRONG_AUTH_OPERATION_ID,
+ new IUsbOperationInternal.Default(),
+ UsbManagerInternal.OS_USB_DISABLE_REASON_LOCKDOWN_MODE,
+ true);
}
}
}
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index e2fb6019f30a..ad0d4f4de3ae 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -16,7 +16,6 @@
package android.telecom.Logging;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,8 +23,13 @@ import android.telecom.Log;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.flags.Flags;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Stores information about a thread's point of entry into that should persist until that thread
@@ -55,7 +59,7 @@ public class Session {
* Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
* if the Session is canceled.
*/
- public static final int UNDEFINED = -1;
+ public static final long UNDEFINED = -1;
public static class Info implements Parcelable {
public final String sessionId;
@@ -129,46 +133,39 @@ public class Session {
}
}
- private String mSessionId;
- private String mShortMethodName;
+ private final String mSessionId;
+ private volatile String mShortMethodName;
private long mExecutionStartTimeMs;
private long mExecutionEndTimeMs = UNDEFINED;
- private Session mParentSession;
- private ArrayList<Session> mChildSessions;
+ private volatile Session mParentSession;
+ private final ArrayList<Session> mChildSessions = new ArrayList<>(5);
private boolean mIsCompleted = false;
- private boolean mIsExternal = false;
- private int mChildCounter = 0;
+ private final boolean mIsExternal;
+ private final AtomicInteger mChildCounter = new AtomicInteger(0);
// True if this is a subsession that has been started from the same thread as the parent
// session. This can happen if Log.startSession(...) is called multiple times on the same
// thread in the case of one Telecom entry point method calling another entry point method.
// In this case, we can just make this subsession "invisible," but still keep track of it so
// that the Log.endSession() calls match up.
- private boolean mIsStartedFromActiveSession = false;
+ private final boolean mIsStartedFromActiveSession;
// Optionally provided info about the method/class/component that started the session in order
// to make Logging easier. This info will be provided in parentheses along with the session.
- private String mOwnerInfo;
+ private final String mOwnerInfo;
// Cache Full Method path so that recursive population of the full method path only needs to
// be calculated once.
- private String mFullMethodPathCache;
+ private volatile String mFullMethodPathCache;
public Session(String sessionId, String shortMethodName, long startTimeMs,
- boolean isStartedFromActiveSession, String ownerInfo) {
- setSessionId(sessionId);
+ boolean isStartedFromActiveSession, boolean isExternal, String ownerInfo) {
+ mSessionId = (sessionId != null) ? sessionId : "???";
setShortMethodName(shortMethodName);
mExecutionStartTimeMs = startTimeMs;
mParentSession = null;
- mChildSessions = new ArrayList<>(5);
mIsStartedFromActiveSession = isStartedFromActiveSession;
+ mIsExternal = isExternal;
mOwnerInfo = ownerInfo;
}
- public void setSessionId(@NonNull String sessionId) {
- if (sessionId == null) {
- mSessionId = "?";
- }
- mSessionId = sessionId;
- }
-
public String getShortMethodName() {
return mShortMethodName;
}
@@ -180,10 +177,6 @@ public class Session {
mShortMethodName = shortMethodName;
}
- public void setIsExternal(boolean isExternal) {
- mIsExternal = isExternal;
- }
-
public boolean isExternal() {
return mIsExternal;
}
@@ -193,13 +186,15 @@ public class Session {
}
public void addChild(Session childSession) {
- if (childSession != null) {
+ if (childSession == null) return;
+ synchronized (mChildSessions) {
mChildSessions.add(childSession);
}
}
public void removeChild(Session child) {
- if (child != null) {
+ if (child == null) return;
+ synchronized (mChildSessions) {
mChildSessions.remove(child);
}
}
@@ -217,7 +212,9 @@ public class Session {
}
public ArrayList<Session> getChildSessions() {
- return mChildSessions;
+ synchronized (mChildSessions) {
+ return new ArrayList<>(mChildSessions);
+ }
}
public boolean isSessionCompleted() {
@@ -259,17 +256,41 @@ public class Session {
return mExecutionEndTimeMs - mExecutionStartTimeMs;
}
- public synchronized String getNextChildId() {
- return String.valueOf(mChildCounter++);
+ public String getNextChildId() {
+ return String.valueOf(mChildCounter.getAndIncrement());
}
- // Builds full session id recursively
+ // Builds full session ID, which incliudes the optional external indicators (E),
+ // base session ID, and the optional sub-session IDs (_X): @[E-]...[ID][_X][_Y]...
private String getFullSessionId() {
- return getFullSessionId(0);
+ if (!Flags.endSessionImprovements()) return getFullSessionIdRecursive(0);
+ int currParentCount = 0;
+ StringBuilder id = new StringBuilder();
+ Session currSession = this;
+ while (currSession != null) {
+ Session parentSession = currSession.getParentSession();
+ if (parentSession != null) {
+ if (currParentCount >= SESSION_RECURSION_LIMIT) {
+ id.insert(0, getSessionId());
+ id.insert(0, TRUNCATE_STRING);
+ android.util.Slog.w(LOG_TAG, "getFullSessionId: Hit iteration limit!");
+ return id.toString();
+ }
+ if (Log.VERBOSE) {
+ id.insert(0, currSession.getSessionId());
+ id.insert(0, SESSION_SEPARATION_CHAR_CHILD);
+ }
+ } else {
+ id.insert(0, currSession.getSessionId());
+ }
+ currSession = parentSession;
+ currParentCount++;
+ }
+ return id.toString();
}
// keep track of calls and bail if we hit the recursion limit
- private String getFullSessionId(int parentCount) {
+ private String getFullSessionIdRecursive(int parentCount) {
if (parentCount >= SESSION_RECURSION_LIMIT) {
// Don't use Telecom's Log.w here or it will cause infinite recursion because it will
// try to add session information to this logging statement, which will cause it to hit
@@ -286,12 +307,12 @@ public class Session {
return mSessionId;
} else {
if (Log.VERBOSE) {
- return parentSession.getFullSessionId(parentCount + 1)
+ return parentSession.getFullSessionIdRecursive(parentCount + 1)
// Append "_X" to subsession to show subsession designation.
+ SESSION_SEPARATION_CHAR_CHILD + mSessionId;
} else {
// Only worry about the base ID at the top of the tree.
- return parentSession.getFullSessionId(parentCount + 1);
+ return parentSession.getFullSessionIdRecursive(parentCount + 1);
}
}
@@ -300,16 +321,18 @@ public class Session {
private Session getRootSession(String callingMethod) {
int currParentCount = 0;
Session topNode = this;
- while (topNode.getParentSession() != null) {
+ Session parentNode = topNode.getParentSession();
+ while (parentNode != null) {
if (currParentCount >= SESSION_RECURSION_LIMIT) {
// Don't use Telecom's Log.w here or it will cause infinite recursion because it
// will try to add session information to this logging statement, which will cause
// it to hit this condition again and so on...
- android.util.Slog.w(LOG_TAG, "getRootSession: Hit recursion limit from "
+ android.util.Slog.w(LOG_TAG, "getRootSession: Hit iteration limit from "
+ callingMethod);
break;
}
- topNode = topNode.getParentSession();
+ topNode = parentNode;
+ parentNode = topNode.getParentSession();
currParentCount++;
}
return topNode;
@@ -320,14 +343,40 @@ public class Session {
return getRootSession("printFullSessionTree").printSessionTree();
}
- // Recursively move down session tree using DFS, but print out each node when it is reached.
private String printSessionTree() {
StringBuilder sb = new StringBuilder();
- printSessionTree(0, sb, 0);
+ if (!Flags.endSessionImprovements()) {
+ printSessionTreeRecursive(0, sb, 0);
+ return sb.toString();
+ }
+ int depth = 0;
+ ArrayDeque<Session> deque = new ArrayDeque<>();
+ deque.add(this);
+ while (!deque.isEmpty()) {
+ Session node = deque.pollFirst();
+ sb.append("\t".repeat(depth));
+ sb.append(node.toString());
+ sb.append("\n");
+ if (depth >= SESSION_RECURSION_LIMIT) {
+ sb.append(TRUNCATE_STRING);
+ depth -= 1;
+ continue;
+ }
+ List<Session> childSessions = node.getChildSessions().reversed();
+ if (!childSessions.isEmpty()) {
+ depth += 1;
+ for (Session child : childSessions) {
+ deque.addFirst(child);
+ }
+ } else {
+ depth -= 1;
+ }
+ }
return sb.toString();
}
- private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
+ // Recursively move down session tree using DFS, but print out each node when it is reached.
+ private void printSessionTreeRecursive(int tabI, StringBuilder sb, int currChildCount) {
// Prevent infinite recursion.
if (currChildCount >= SESSION_RECURSION_LIMIT) {
// Don't use Telecom's Log.w here or it will cause infinite recursion because it will
@@ -343,26 +392,85 @@ public class Session {
for (int i = 0; i <= tabI; i++) {
sb.append("\t");
}
- child.printSessionTree(tabI + 1, sb, currChildCount + 1);
+ child.printSessionTreeRecursive(tabI + 1, sb, currChildCount + 1);
}
}
- // Recursively concatenate mShortMethodName with the parent Sessions to create full method
- // path. if truncatePath is set to true, all other external sessions (except for the most
- // recent) will be truncated to "..."
+ //
+
+ /**
+ * Concatenate the short method name with the parent Sessions to create full method path.
+ * @param truncatePath if truncatePath is set to true, all other external sessions (except for
+ * the most recent) will be truncated to "..."
+ * @return The full method path associated with this Session.
+ */
+ @VisibleForTesting
public String getFullMethodPath(boolean truncatePath) {
StringBuilder sb = new StringBuilder();
- getFullMethodPath(sb, truncatePath, 0);
+ if (!Flags.endSessionImprovements()) {
+ getFullMethodPathRecursive(sb, truncatePath, 0);
+ return sb.toString();
+ }
+ // Check to see if the session has been renamed yet. If it has not, then the session
+ // has not been continued.
+ Session parentSession = getParentSession();
+ boolean isSessionStarted = parentSession == null
+ || !getShortMethodName().equals(parentSession.getShortMethodName());
+ int depth = 0;
+ Session currSession = this;
+ while (currSession != null) {
+ String cache = currSession.mFullMethodPathCache;
+ // Return cached value for method path. When returning the truncated path, recalculate
+ // the full path without using the cached value.
+ if (!TextUtils.isEmpty(cache) && !truncatePath) {
+ sb.insert(0, cache);
+ return sb.toString();
+ }
+
+ parentSession = currSession.getParentSession();
+ // Encapsulate the external session's method name so it is obvious what part of the
+ // session is external or truncate it if we do not want the entire history.
+ if (currSession.isExternal()) {
+ if (truncatePath) {
+ sb.insert(0, TRUNCATE_STRING);
+ } else {
+ sb.insert(0, ")");
+ sb.insert(0, currSession.getShortMethodName());
+ sb.insert(0, "(");
+ }
+ } else {
+ sb.insert(0, currSession.getShortMethodName());
+ }
+ if (parentSession != null) {
+ sb.insert(0, SUBSESSION_SEPARATION_CHAR);
+ }
+
+ if (depth >= SESSION_RECURSION_LIMIT) {
+ // Don't use Telecom's Log.w here or it will cause infinite recursion because it
+ // will try to add session information to this logging statement, which will cause
+ // it to hit this condition again and so on...
+ android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit iteration limit!");
+ sb.insert(0, TRUNCATE_STRING);
+ return sb.toString();
+ }
+ currSession = parentSession;
+ depth++;
+ }
+ if (isSessionStarted && !truncatePath) {
+ // Cache the full method path for this node so that we do not need to calculate it
+ // again in the future.
+ mFullMethodPathCache = sb.toString();
+ }
return sb.toString();
}
- private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
+ private synchronized void getFullMethodPathRecursive(StringBuilder sb, boolean truncatePath,
int parentCount) {
if (parentCount >= SESSION_RECURSION_LIMIT) {
// Don't use Telecom's Log.w here or it will cause infinite recursion because it will
// try to add session information to this logging statement, which will cause it to hit
// this condition again and so on...
- android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+ android.util.Slog.w(LOG_TAG, "getFullMethodPathRecursive: Hit recursion limit!");
sb.append(TRUNCATE_STRING);
return;
}
@@ -378,7 +486,7 @@ public class Session {
// Check to see if the session has been renamed yet. If it has not, then the session
// has not been continued.
isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
- parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1);
+ parentSession.getFullMethodPathRecursive(sb, truncatePath, parentCount + 1);
sb.append(SUBSESSION_SEPARATION_CHAR);
}
// Encapsulate the external session's method name so it is obvious what part of the session
@@ -409,14 +517,14 @@ public class Session {
@Override
public int hashCode() {
- int result = mSessionId != null ? mSessionId.hashCode() : 0;
- result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0);
- result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32));
- result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32));
+ int result = mSessionId.hashCode();
+ result = 31 * result + mShortMethodName.hashCode();
+ result = 31 * result + Long.hashCode(mExecutionStartTimeMs);
+ result = 31 * result + Long.hashCode(mExecutionEndTimeMs);
result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0);
- result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0);
+ result = 31 * result + mChildSessions.hashCode();
result = 31 * result + (mIsCompleted ? 1 : 0);
- result = 31 * result + mChildCounter;
+ result = 31 * result + mChildCounter.hashCode();
result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0);
result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0);
return result;
@@ -432,23 +540,13 @@ public class Session {
if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false;
if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false;
if (mIsCompleted != session.mIsCompleted) return false;
- if (mChildCounter != session.mChildCounter) return false;
+ if (!(mChildCounter.get() == session.mChildCounter.get())) return false;
if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false;
- if (mSessionId != null ?
- !mSessionId.equals(session.mSessionId) : session.mSessionId != null)
- return false;
- if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName)
- : session.mShortMethodName != null)
- return false;
- if (mParentSession != null ? !mParentSession.equals(session.mParentSession)
- : session.mParentSession != null)
- return false;
- if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions)
- : session.mChildSessions != null)
- return false;
- return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo)
- : session.mOwnerInfo == null;
-
+ if (!Objects.equals(mSessionId, session.mSessionId)) return false;
+ if (!Objects.equals(mShortMethodName, session.mShortMethodName)) return false;
+ if (!Objects.equals(mParentSession, session.mParentSession)) return false;
+ if (!Objects.equals(mChildSessions, session.mChildSessions)) return false;
+ return Objects.equals(mOwnerInfo, session.mOwnerInfo);
}
@Override
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 9d17219c1ae4..00e344c67cc5 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -27,6 +27,7 @@ import android.telecom.Log;
import android.util.Base64;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.flags.Flags;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -36,10 +37,16 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
- * TODO: Create better Sessions Documentation
+ * SessionManager manages the active sessions in a HashMap, which maps the active thread(s) to the
+ * associated {@link Session}s.
+ * <p>
+ * Note: Sessions assume that session structure modification is synchronized on this object - only
+ * one thread can modify the structure of any Session at one time. Printing the current session to
+ * the log is not synchronized because we should not clean up a session chain while printing from
+ * another Thread. Either the Session chain is still active and can not be cleaned up yet, or the
+ * Session chain has ended and we are cleaning up.
* @hide
*/
-
public class SessionManager {
// Currently using 3 letters, So don't exceed 64^3
@@ -54,11 +61,11 @@ public class SessionManager {
private Context mContext;
@VisibleForTesting
- public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
+ public final ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(64);
@VisibleForTesting
public java.lang.Runnable mCleanStaleSessions = () ->
cleanupStaleSessions(getSessionCleanupTimeoutMs());
- private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
+ private final Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
// Overridden in LogTest to skip query to ContentProvider
private interface ISessionCleanupTimeoutMs {
@@ -83,7 +90,7 @@ public class SessionManager {
};
// Usage is synchronized on this class.
- private List<ISessionListener> mSessionListeners = new ArrayList<>();
+ private final List<ISessionListener> mSessionListeners = new ArrayList<>();
public interface ISessionListener {
/**
@@ -110,10 +117,19 @@ public class SessionManager {
}
private synchronized void resetStaleSessionTimer() {
- mSessionCleanupHandler.removeCallbacksAndMessages(null);
- // Will be null in Log Testing
- if (mCleanStaleSessions != null) {
- mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs());
+ if (!Flags.endSessionImprovements()) {
+ mSessionCleanupHandler.removeCallbacksAndMessages(null);
+ // Will be null in Log Testing
+ if (mCleanStaleSessions != null) {
+ mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
+ getSessionCleanupTimeoutMs());
+ }
+ } else {
+ if (mCleanStaleSessions != null
+ && !mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) {
+ mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
+ getSessionCleanupTimeoutMs());
+ }
}
}
@@ -147,13 +163,11 @@ public class SessionManager {
Session childSession = createSubsession(true);
continueSession(childSession, shortMethodName);
return;
- } else {
- // Only Log that we are starting the parent session.
- Log.d(LOGGING_TAG, Session.START_SESSION);
}
Session newSession = new Session(getNextSessionID(), shortMethodName,
- System.currentTimeMillis(), false, callerIdentification);
+ System.currentTimeMillis(), false, false, callerIdentification);
mSessionMapper.put(threadId, newSession);
+ Log.d(LOGGING_TAG, Session.START_SESSION);
}
/**
@@ -179,17 +193,16 @@ public class SessionManager {
}
// Create Session from Info and add to the sessionMapper under this ID.
- Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
- sessionInfo.methodPath, System.currentTimeMillis(),
- false /*isStartedFromActiveSession*/, sessionInfo.ownerInfo);
- externalSession.setIsExternal(true);
+ sessionInfo.methodPath, System.currentTimeMillis(), false, true,
+ sessionInfo.ownerInfo);
// Mark the external session as already completed, since we have no way of knowing when
// the external session actually has completed.
externalSession.markSessionCompleted(Session.UNDEFINED);
// Track the external session with the SessionMapper so that we can create and continue
// an active subsession based on it.
mSessionMapper.put(threadId, externalSession);
+ Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
// Create a subsession from this external Session parent node
Session childSession = createSubsession();
continueSession(childSession, shortMethodName);
@@ -226,13 +239,12 @@ public class SessionManager {
// Start execution time of the session will be overwritten in continueSession(...).
Session newSubsession = new Session(threadSession.getNextChildId(),
threadSession.getShortMethodName(), System.currentTimeMillis(),
- isStartedFromActiveSession, threadSession.getOwnerInfo());
+ isStartedFromActiveSession, false, threadSession.getOwnerInfo());
threadSession.addChild(newSubsession);
newSubsession.setParentSession(threadSession);
if (!isStartedFromActiveSession) {
- Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
- newSubsession.toString());
+ Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION);
} else {
Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
" (Invisible subsession)");
@@ -273,7 +285,7 @@ public class SessionManager {
}
subsession.markSessionCompleted(Session.UNDEFINED);
- endParentSessions(subsession);
+ cleanupSessionTreeAndNotify(subsession);
}
/**
@@ -328,7 +340,7 @@ public class SessionManager {
// Remove after completed so that reference still exists for logging the end events
Session parentSession = completedSession.getParentSession();
mSessionMapper.remove(threadId);
- endParentSessions(completedSession);
+ cleanupSessionTreeAndNotify(completedSession);
// If this subsession was started from a parent session using Log.startSession, return the
// ThreadID back to the parent after completion.
if (parentSession != null && !parentSession.isSessionCompleted() &&
@@ -337,8 +349,49 @@ public class SessionManager {
}
}
+ /**
+ * Move up the session tree and remove completed sessions until we either hit a session that was
+ * not completed yet or we reach the root node. Once we reach the root node, we will report the
+ * session times to session complete listeners.
+ * @param session The Session to clean up.
+ */
+ private void cleanupSessionTreeAndNotify(Session session) {
+ if (session == null) return;
+ if (!Flags.endSessionImprovements()) {
+ endParentSessionsRecursive(session);
+ return;
+ }
+ Session currSession = session;
+ // Traverse upwards and unlink until we either hit the root node or a node that isn't
+ // complete yet.
+ while (currSession != null) {
+ if (!currSession.isSessionCompleted() || !currSession.getChildSessions().isEmpty()) {
+ // We will return once the active session is completed.
+ return;
+ }
+ Session parentSession = currSession.getParentSession();
+ currSession.setParentSession(null);
+ // The session is either complete when we have reached the top node or we have reached
+ // the node where the parent is external. We only want to report the time it took to
+ // complete the local session, so for external nodes, report finished when the sub-node
+ // completes.
+ boolean reportSessionComplete =
+ (parentSession == null && !currSession.isExternal())
+ || (parentSession != null && parentSession.isExternal());
+ if (parentSession != null) parentSession.removeChild(currSession);
+ if (reportSessionComplete) {
+ long fullSessionTimeMs = System.currentTimeMillis()
+ - currSession.getExecutionStartTimeMilliseconds();
+ Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
+ + " ms): " + currSession);
+ notifySessionCompleteListeners(currSession.getShortMethodName(), fullSessionTimeMs);
+ }
+ currSession = parentSession;
+ }
+ }
+
// Recursively deletes all complete parent sessions of the current subsession if it is a leaf.
- private void endParentSessions(Session subsession) {
+ private void endParentSessionsRecursive(Session subsession) {
// Session is not completed or not currently a leaf, so we can not remove because a child is
// still running
if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
@@ -355,7 +408,7 @@ public class SessionManager {
System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
}
- endParentSessions(parentSession);
+ endParentSessionsRecursive(parentSession);
} else {
// All of the subsessions have been completed and it is time to report on the full
// running time of the session.
@@ -370,8 +423,10 @@ public class SessionManager {
}
private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) {
- for (ISessionListener l : mSessionListeners) {
- l.sessionComplete(methodName, sessionTimeMs);
+ synchronized (mSessionListeners) {
+ for (ISessionListener l : mSessionListeners) {
+ l.sessionComplete(methodName, sessionTimeMs);
+ }
}
}
@@ -380,8 +435,8 @@ public class SessionManager {
return currentSession != null ? currentSession.toString() : "";
}
- public synchronized void registerSessionListener(ISessionListener l) {
- if (l != null) {
+ public void registerSessionListener(ISessionListener l) {
+ synchronized (mSessionListeners) {
mSessionListeners.add(l);
}
}
@@ -425,25 +480,30 @@ public class SessionManager {
@VisibleForTesting
public synchronized void cleanupStaleSessions(long timeoutMs) {
- String logMessage = "Stale Sessions Cleaned:\n";
+ StringBuilder logMessage = new StringBuilder("Stale Sessions Cleaned:");
boolean isSessionsStale = false;
long currentTimeMs = System.currentTimeMillis();
// Remove references that are in the Session Mapper (causing GC to occur) on
- // sessions that are lasting longer than LOGGING_SESSION_TIMEOUT_MS.
+ // sessions that are lasting longer than DEFAULT_SESSION_TIMEOUT_MS.
// If this occurs, then there is most likely a Session active that never had
// Log.endSession called on it.
for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it =
mSessionMapper.entrySet().iterator(); it.hasNext(); ) {
ConcurrentHashMap.Entry<Integer, Session> entry = it.next();
Session session = entry.getValue();
- if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) {
+ long runTime = currentTimeMs - session.getExecutionStartTimeMilliseconds();
+ if (runTime > timeoutMs) {
it.remove();
- logMessage += session.printFullSessionTree() + "\n";
+ logMessage.append("\n");
+ logMessage.append("[");
+ logMessage.append(runTime);
+ logMessage.append("ms] ");
+ logMessage.append(session.printFullSessionTree());
isSessionsStale = true;
}
}
if (isSessionsStale) {
- Log.w(LOGGING_TAG, logMessage);
+ Log.w(LOGGING_TAG, logMessage.toString());
} else {
Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 47f6764dba98..02999c81250a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -253,7 +253,6 @@ public class CarrierConfigManager {
*
* The default value is true.
*/
- @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
"additional_settings_caller_id_visibility_bool";
@@ -263,7 +262,6 @@ public class CarrierConfigManager {
*
* The default value is true.
*/
- @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
"additional_settings_call_waiting_visibility_bool";
@@ -10558,7 +10556,6 @@ public class CarrierConfigManager {
* @see SubscriptionInfo#getServiceCapabilities()
* @see SubscriptionManager.OnSubscriptionsChangedListener
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
"cellular_service_capabilities_int_array";
/**
@@ -11110,7 +11107,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
- sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, true);
+ sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false);
/* Default value is 1 hour. */
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 1089602934fd..d164c8851f5b 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -951,7 +951,6 @@ public class SubscriptionInfo implements Parcelable {
* @see SubscriptionManager#SERVICE_CAPABILITY_DATA
*/
@NonNull
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
}
@@ -1829,7 +1828,6 @@ public class SubscriptionInfo implements Parcelable {
* @throws IllegalArgumentException when any capability is not supported.
*/
@NonNull
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public Builder setServiceCapabilities(
@NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
int combinedCapabilities = 0;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 6faef7ecfa1b..377e5f2e7db2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1422,7 +1422,6 @@ public class SubscriptionManager {
*
* @see TelephonyManager#isDeviceVoiceCapable()
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public static final int SERVICE_CAPABILITY_VOICE = 1;
/**
@@ -1440,13 +1439,11 @@ public class SubscriptionManager {
*
* @see TelephonyManager#isDeviceSmsCapable()
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public static final int SERVICE_CAPABILITY_SMS = 2;
/**
* Represents a value indicating the data calling capabilities of a subscription.
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public static final int SERVICE_CAPABILITY_DATA = 3;
/**
@@ -3474,14 +3471,62 @@ public class SubscriptionManager {
@SystemApi
public boolean canManageSubscription(@NonNull SubscriptionInfo info,
@NonNull String packageName) {
+ if (Flags.hsumPackageManager()) {
+ return canManageSubscriptionAsUser(info, packageName, mContext.getUser());
+ } else {
+ if (info == null || info.getAccessRules() == null || packageName == null) {
+ return false;
+ }
+ PackageManager packageManager = mContext.getPackageManager();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (PackageManager.NameNotFoundException e) {
+ logd("Unknown package: " + packageName);
+ return false;
+ }
+ for (UiccAccessRule rule : info.getAccessRules()) {
+ if (rule.getCarrierPrivilegeStatus(packageInfo)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks whether the given app is authorized to manage the given subscription for given user.
+ *
+ * <p>An app can only be authorized if it is available to the given user and included in the
+ * {@link android.telephony.UiccAccessRule} of the {@link android.telephony.SubscriptionInfo}
+ * with the access status.
+ *
+ * <p>Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
+ * true). To check for permissions for non-embedded subscription as well,
+ * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
+ *
+ * @param info The subscription to check.
+ * @param packageName Package name of the app to check.
+ * @param user UserHandle to check
+ * @return whether the app is authorized to manage this subscription per its access rules.
+ *
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * @hide
+ */
+ public boolean canManageSubscriptionAsUser(@NonNull SubscriptionInfo info,
+ @NonNull String packageName, @NonNull UserHandle user) {
if (info == null || info.getAccessRules() == null || packageName == null) {
return false;
}
- PackageManager packageManager = mContext.getPackageManager();
+ PackageManager pm = mContext.getUser().equals(user)
+ ? mContext.getPackageManager()
+ : mContext.createContextAsUser(user, 0).getPackageManager();
PackageInfo packageInfo;
try {
- packageInfo = packageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
+ packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
logd("Unknown package: " + packageName);
return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fee45873502b..a7fe0cb0940c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2127,7 +2127,6 @@ public class TelephonyManager {
* <p>On some devices, this settings activity may not exist. Callers should ensure that this
* case is appropriately handled.
*/
- @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
"android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
@@ -6934,7 +6933,6 @@ public class TelephonyManager {
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public boolean isDeviceVoiceCapable() {
return isVoiceCapable();
}
@@ -6974,7 +6972,6 @@ public class TelephonyManager {
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
- @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
public boolean isDeviceSmsCapable() {
return isSmsCapable();
}
@@ -8781,13 +8778,14 @@ public class TelephonyManager {
* Authentication error, no memory space available in EFMUK
*
* @throws UnsupportedOperationException If the device does not have
- * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+ * authType.
*/
// TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
// READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
// it's not public API.
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
- public String getIccAuthentication(int appType,@AuthType int authType, String data) {
+ public String getIccAuthentication(int appType, @AuthType int authType, String data) {
return getIccAuthentication(getSubId(), appType, authType, data);
}
@@ -8812,10 +8810,14 @@ public class TelephonyManager {
* Key freshness failure
* Authentication error, no memory space available
* Authentication error, no memory space available in EFMUK
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+ * authType.
* @hide
*/
@UnsupportedAppUsage
- public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) {
+ public String getIccAuthentication(int subId, int appType, @AuthType int authType,
+ String data) {
try {
IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index f2e34257ef01..9ce8e807f612 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -352,7 +352,8 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryActivity"
- android:exported="true">
+ android:exported="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -363,7 +364,8 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryActivity"
- android:exported="true">
+ android:exported="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 0375f66069c3..d9295dd17dd0 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -515,33 +515,27 @@ public class StagedInstallInternalTest {
Install.single(APEX_V2));
}
- @Test
- public void testGetStagedModuleNames() throws Exception {
- // Before staging a session
- String[] result = getPackageManagerNative().getStagedApexModuleNames();
- assertThat(result).hasLength(0);
- // Stage an apex
- int sessionId = Install.single(APEX_V2).setStaged().commit();
- result = getPackageManagerNative().getStagedApexModuleNames();
- assertThat(result).hasLength(1);
- assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME});
- // Abandon the session
- InstallUtils.openPackageInstallerSession(sessionId).abandon();
- result = getPackageManagerNative().getStagedApexModuleNames();
- assertThat(result).hasLength(0);
+ private StagedApexInfo findStagedApexInfo(StagedApexInfo[] infos, String moduleName) {
+ for (StagedApexInfo info: infos) {
+ if (info.moduleName.equals(moduleName)) {
+ return info;
+ }
+ }
+ return null;
}
@Test
- public void testGetStagedApexInfo() throws Exception {
- // Ask for non-existing module
- StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
- assertThat(result).isNull();
+ public void testGetStagedApexInfos() throws Exception {
+ // Not found before staging
+ StagedApexInfo[] result = getPackageManagerNative().getStagedApexInfos();
+ assertThat(findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME)).isNull();
// Stage an apex
int sessionId = Install.single(TEST_APEX_CLASSPATH).setStaged().commit();
// Query proper module name
- result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
- assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
- assertThat(result.hasClassPathJars).isTrue();
+ result = getPackageManagerNative().getStagedApexInfos();
+ StagedApexInfo found = findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME);
+ assertThat(found).isNotNull();
+ assertThat(found.hasClassPathJars).isTrue();
InstallUtils.openPackageInstallerSession(sessionId).abandon();
}
@@ -573,14 +567,15 @@ public class StagedInstallInternalTest {
int sessionId = Install.single(APEX_V2).setStaged().commit();
ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class);
verify(observer, timeout(5000)).onApexStaged(captor.capture());
- assertThat(captor.getValue().stagedApexModuleNames).isEqualTo(
- new String[] {SHIM_APEX_PACKAGE_NAME});
+ StagedApexInfo found =
+ findStagedApexInfo(captor.getValue().stagedApexInfos, SHIM_APEX_PACKAGE_NAME);
+ assertThat(found).isNotNull();
// Abandon and verify observer is called
Mockito.clearInvocations(observer);
InstallUtils.openPackageInstallerSession(sessionId).abandon();
verify(observer, timeout(5000)).onApexStaged(captor.capture());
- assertThat(captor.getValue().stagedApexModuleNames).hasLength(0);
+ assertThat(captor.getValue().stagedApexInfos).hasLength(0);
}
@Test
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index f1fc503c8556..97abcd77e6b9 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -592,23 +592,15 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
@Test
- public void testGetStagedModuleNames() throws Exception {
- assumeTrue("Device does not support updating APEX",
- mHostUtils.isApexUpdateSupported());
-
- runPhase("testGetStagedModuleNames");
- }
-
- @Test
@LargeTest
- public void testGetStagedApexInfo() throws Exception {
+ public void testGetStagedApexInfos() throws Exception {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
pushTestApex(APEXD_TEST_APEX);
getDevice().reboot();
- runPhase("testGetStagedApexInfo");
+ runPhase("testGetStagedApexInfos");
}
@Test
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4cb7c91b2451..7e0bbc4b3e50 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -70,7 +70,6 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri;
-import android.net.vcn.Flags;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
@@ -85,7 +84,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -104,7 +102,6 @@ import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -122,8 +119,6 @@ import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
@@ -285,8 +280,6 @@ public class VcnManagementServiceTest {
@Before
public void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_MAIN_USER);
-
doNothing()
.when(mMockContext)
.enforceCallingOrSelfPermission(
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index edad67896e8e..421e1ad20b78 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -34,14 +34,12 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.FeatureFlags;
-import android.net.vcn.Flags;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyManager;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -49,7 +47,6 @@ import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
import org.junit.Before;
-import org.junit.Rule;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -57,8 +54,6 @@ import java.util.Set;
import java.util.UUID;
public abstract class NetworkEvaluationTestBase {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
protected static final String SSID = "TestWifi";
protected static final String SSID_OTHER = "TestWifiOther";
protected static final String PLMN_ID = "123456";
@@ -120,10 +115,6 @@ public abstract class NetworkEvaluationTestBase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS);
- mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE);
- mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP);
-
when(mNetwork.getNetId()).thenReturn(-1);
mTestLooper = new TestLooper();
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 4920f7b41e3f..a5ff4964b0a4 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -8,7 +8,7 @@ package {
// OWNER: g/ravenwood
// Bug component: 25698
- default_team: "trendy_team_framework_backstage_power",
+ default_team: "trendy_team_ravenwood",
}
// Visibility only for ravenwood prototype uses.