summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp2
-rw-r--r--TEST_MAPPING6
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java96
-rw-r--r--core/api/current.txt12
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt26
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/animation/AnimationHandler.java10
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java22
-rw-r--r--core/java/android/app/ApplicationPackageManager.java24
-rw-r--r--core/java/android/app/AutomaticZenRule.java12
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/Notification.java74
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java37
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManagerHelper.java4
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java4
-rw-r--r--core/java/android/app/jank/TEST_MAPPING10
-rw-r--r--core/java/android/app/notification.aconfig14
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java2
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl6
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensor.java47
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl19
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java202
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorConfig.java35
-rw-r--r--core/java/android/content/Intent.java10
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java36
-rw-r--r--core/java/android/content/pm/SystemFeaturesCache.java5
-rw-r--r--core/java/android/content/pm/TEST_MAPPING44
-rw-r--r--core/java/android/content/pm/multiuser.aconfig10
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java13
-rw-r--r--core/java/android/hardware/display/DisplayManager.java5
-rw-r--r--core/java/android/hardware/serial/OWNERS3
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java3
-rw-r--r--core/java/android/os/IpcDataCache.java5
-rw-r--r--core/java/android/os/Parcel.java78
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--core/java/android/permission/PermissionManager.java6
-rw-r--r--core/java/android/provider/Settings.java47
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java37
-rw-r--r--core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl2
-rw-r--r--core/java/android/service/dreams/DreamService.java101
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java2
-rw-r--r--core/java/android/service/notification/TEST_MAPPING5
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java24
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java7
-rw-r--r--core/java/android/view/Display.java14
-rw-r--r--core/java/android/view/DisplayInfo.java3
-rw-r--r--core/java/android/view/IWindowManager.aidl19
-rw-r--r--core/java/android/view/ViewRootImpl.java23
-rw-r--r--core/java/android/view/WindowManager.java38
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java42
-rw-r--r--core/java/android/view/contentcapture/flags/content_capture_flags.aconfig1
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java14
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java188
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig21
-rw-r--r--core/java/android/window/BackMotionEvent.java22
-rw-r--r--core/java/android/window/BackTouchTracker.java9
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java4
-rw-r--r--core/java/android/window/DesktopModeFlags.java2
-rw-r--r--core/java/android/window/IWindowOrganizerController.aidl11
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java6
-rw-r--r--core/java/android/window/WindowContainerTransaction.java62
-rw-r--r--core/java/android/window/WindowOrganizer.java21
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig8
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig34
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig32
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java11
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserContentManager.java56
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialog.java37
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java1
-rw-r--r--core/java/com/android/internal/policy/DesktopModeCompatUtils.java60
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java49
-rw-r--r--core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java118
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java79
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java65
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java127
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java314
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java140
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java146
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java32
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java7
-rw-r--r--core/jni/android_os_Parcel.cpp85
-rw-r--r--core/jni/android_view_InputChannel.cpp29
-rw-r--r--core/jni/android_window_ScreenCapture.cpp28
-rw-r--r--core/proto/android/os/incident.proto7
-rw-r--r--core/proto/android/providers/settings/secure.proto6
-rw-r--r--core/proto/android/providers/settings/system.proto11
-rw-r--r--core/proto/android/server/Android.bp28
-rw-r--r--core/proto/android/server/bluetooth_manager_service.proto49
-rw-r--r--core/res/res/layout/accessibility_autoclick_type_panel.xml21
-rw-r--r--core/res/res/layout/notification_2025_action_list.xml1
-rw-r--r--core/res/res/layout/notification_2025_conversation_icon_container.xml3
-rw-r--r--core/res/res/layout/notification_2025_reply_history_container.xml14
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml3
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_call.xml28
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_conversation.xml35
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml3
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_messaging.xml37
-rw-r--r--core/res/res/layout/notification_2025_template_compact_heads_up_base.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_base.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_big_picture.xml7
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_big_text.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_call.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_conversation.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_inbox.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_media.xml4
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_messaging.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_progress.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_heads_up_base.xml2
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/dimens.xml5
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java2
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java4
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java12
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java403
-rw-r--r--core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java3
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java6
-rw-r--r--core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java4
-rw-r--r--core/tests/coretests/src/android/os/ParcelTest.java61
-rw-r--r--core/tests/coretests/src/android/provider/FontsContractTest.java4
-rw-r--r--core/tests/coretests/src/android/view/PendingInsetsControllerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java8
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java4
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java44
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java3
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java2
-rw-r--r--core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java11
-rw-r--r--core/tests/coretests/src/android/window/BackTouchTrackerTest.kt2
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java51
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java39
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java4
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java4
-rw-r--r--core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java8
-rw-r--r--data/etc/preinstalled-packages-platform.xml6
-rw-r--r--graphics/java/android/graphics/Bitmap.java8
-rw-r--r--graphics/java/android/graphics/Gainmap.java12
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml31
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml12
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml19
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java139
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt200
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt84
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt88
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java139
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java59
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java123
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt101
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt67
-rw-r--r--libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt168
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDoubleTapHelperTest.java163
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt391
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt42
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt171
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt74
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt106
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java69
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java132
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt94
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt8
-rw-r--r--location/java/android/location/flags/location.aconfig18
-rw-r--r--media/java/android/media/MediaRouter.java12
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig10
-rw-r--r--media/java/android/media/quality/Android.bp27
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl2
-rw-r--r--media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl2
-rw-r--r--media/java/android/media/quality/include/quality/MediaQualityManager.h127
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java6
-rw-r--r--packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java7
-rw-r--r--packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java12
-rw-r--r--packages/PackageInstaller/TEST_MAPPING44
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt28
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt24
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt69
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt191
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt31
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java65
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java2
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt1
-rw-r--r--packages/SettingsLib/IllustrationPreference/res/values/strings.xml6
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java14
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java4
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt38
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt50
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt25
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt1
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt34
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml26
-rw-r--r--packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java42
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt (renamed from packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt)4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java34
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt90
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt300
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java8
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java10
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java2
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java19
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt124
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/OWNERS4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java49
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java12
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java51
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java24
-rw-r--r--packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java2
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/TEST_OWNERS3
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java2
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig10
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt19
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt18
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt45
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt21
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt83
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt159
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt113
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt76
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt16
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt4
-rw-r--r--packages/SystemUI/compose/scene/tests/Android.bp1
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json624
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json644
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json)332
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json434
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json)186
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json)424
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json314
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json224
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json584
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json624
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json544
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json424
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json374
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json744
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json304
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json254
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json654
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json644
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json444
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json324
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json244
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt81
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt9
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt46
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt108
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt19
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt48
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt120
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt201
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt27
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt296
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt193
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt61
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java27
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt18
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt65
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt132
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt8
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt12
-rw-r--r--packages/SystemUI/res/layout/bluetooth_device_item.xml6
-rw-r--r--packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml38
-rw-r--r--packages/SystemUI/res/layout/notification_2025_info.xml37
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml15
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml15
-rw-r--r--packages/SystemUI/res/layout/promoted_notification_info.xml15
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt170
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt404
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java255
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt107
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt135
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OWNERS4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt241
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java156
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java383
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt297
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt (renamed from packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt)13
-rw-r--r--ravenwood/TEST_MAPPING10
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java10
-rw-r--r--ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java2
-rw-r--r--ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt4
-rw-r--r--ravenwood/tests/resapk_test/Android.bp3
-rw-r--r--ravenwood/tests/resapk_test/apk/Android.bp8
-rw-r--r--ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml8
-rw-r--r--ravenwood/tests/resapk_test/apk/res/values/strings.xml5
-rw-r--r--ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java91
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java42
-rw-r--r--services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java31
-rw-r--r--services/companion/java/com/android/server/companion/virtual/SensorController.java36
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java13
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/AppPermissionTracker.java1
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java18
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java68
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java39
-rw-r--r--services/core/java/com/android/server/am/OWNERS2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java11
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java8
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java16
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsRegistry.java5
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java2
-rw-r--r--services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java (renamed from services/core/java/com/android/server/appop/HistoricalRegistry.java)20
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java109
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java6
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayGroup.java12
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java17
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java6
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java15
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java37
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java14
-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/ModeChangeObserver.java128
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java5
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java10
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java41
-rw-r--r--services/core/java/com/android/server/location/gnss/hal/GnssNative.java46
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java13
-rw-r--r--services/core/java/com/android/server/notification/TEST_MAPPING5
-rw-r--r--services/core/java/com/android/server/notification/ZenConfigTrimmer.java109
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java15
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING64
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java37
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java9
-rw-r--r--services/core/java/com/android/server/power/Notifier.java52
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java11
-rw-r--r--services/core/java/com/android/server/power/WakeLockLog.java42
-rw-r--r--services/core/java/com/android/server/power/feature/PowerManagerFlags.java12
-rw-r--r--services/core/java/com/android/server/power/feature/power_flags.aconfig7
-rw-r--r--services/core/java/com/android/server/security/CertificateRevocationStatusManager.java5
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java153
-rw-r--r--services/core/java/com/android/server/sensors/OWNERS1
-rw-r--r--services/core/java/com/android/server/sensors/SensorManagerInternal.java18
-rw-r--r--services/core/java/com/android/server/sensors/SensorService.java16
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java40
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java17
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java10
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java167
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java5
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java8
-rw-r--r--services/core/java/com/android/server/wm/DeferredDisplayUpdater.java2
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java57
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java28
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java86
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java87
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java39
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java25
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java50
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java102
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java21
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java43
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java53
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java43
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java152
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java3
-rw-r--r--services/core/jni/Android.bp3
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp15
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp36
-rw-r--r--services/core/jni/com_android_server_sensor_SensorService.cpp49
-rw-r--r--services/core/jni/gnss/Android.bp5
-rw-r--r--services/core/jni/gnss/Gnss.cpp11
-rw-r--r--services/core/jni/gnss/Gnss.h2
-rw-r--r--services/core/jni/gnss/GnssAssistance.cpp2047
-rw-r--r--services/core/jni/gnss/GnssAssistance.h135
-rw-r--r--services/core/jni/gnss/GnssAssistanceCallback.cpp48
-rw-r--r--services/core/jni/gnss/GnssAssistanceCallback.h48
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java44
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java3
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java4
-rw-r--r--services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java26
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java98
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java6
-rw-r--r--services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java55
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java38
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java378
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java (renamed from services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java)0
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java80
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java78
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java85
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java2
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java2
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java50
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/PinnerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java153
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java8
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java8
-rw-r--r--services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java10
-rw-r--r--services/tests/uiservicestests/Android.bp83
-rw-r--r--services/tests/uiservicestests/AndroidTest.xml1
-rw-r--r--services/tests/uiservicestests/notification-tests.xml30
-rw-r--r--services/tests/uiservicestests/notification-zen-tests.xml30
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java11
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java46
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java124
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java41
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java9
-rw-r--r--services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java62
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java102
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java84
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java189
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java207
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java87
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java152
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java54
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java4
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java17
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java2
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java9
-rw-r--r--telephony/java/android/telephony/TelephonyFrameworkInitializer.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java4
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java4
-rw-r--r--telephony/java/android/telephony/data/DataService.java3
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java12
-rw-r--r--tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java6
-rw-r--r--tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java523
-rw-r--r--tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt4
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java10
-rw-r--r--tests/testables/tests/src/android/testing/TestableLooperTest.java4
-rw-r--r--tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java2
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java8
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java2
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java4
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java8
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java5
-rw-r--r--wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java2
958 files changed, 27823 insertions, 9350 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 4d9fdf041d58..12781e404ba9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -265,6 +265,7 @@ java_aconfig_library {
cc_aconfig_library {
name: "com.android.window.flags.window-aconfig_flags_c_lib",
aconfig_declarations: "com.android.window.flags.window-aconfig",
+ host_supported: true,
}
// DeviceStateManager
@@ -587,7 +588,6 @@ aconfig_declarations {
java_aconfig_library {
name: "android.view.inputmethod.flags-aconfig-java",
aconfig_declarations: "android.view.inputmethod.flags-aconfig",
- host_supported: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e469f167d32f..ce0da7e21071 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,12 @@
"name": "FrameworksUiServicesTests"
},
{
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
+ },
+ {
"name": "FrameworksInputMethodSystemServerTests_server_inputmethod"
},
{
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 876274ecc32e..aae5bb31273b 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -126,3 +126,15 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "tune_quota_window_default_parameters"
+ namespace: "backstage_power"
+ description: "Tune default active/exempted bucket quota parameters"
+ bug: "401767691"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 54d337eded7d..a9c4a1501dd8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -360,13 +360,13 @@ public final class QuotaController extends StateController {
/** How much time each app will have to run jobs within their standby bucket window. */
private final long[] mAllowedTimePerPeriodMs = new long[]{
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
0, // NEVER
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
};
/**
@@ -3178,9 +3178,11 @@ public final class QuotaController extends StateController {
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ // Legacy default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
10 * 60 * 1000L; // 10 minutes
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ // Legacy default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3192,14 +3194,26 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
10 * 60 * 1000L; // 10 minutes
+
+ // Current default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ // Current default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ 20 * 60 * 1000L; // 20 minutes
+
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
// Legacy default window size for EXEMPTED bucket
+ // EXEMPT apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
// Legacy default window size for ACTIVE bucket
+ // ACTIVE apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
// Legacy default window size for WORKING bucket
private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
@@ -3216,6 +3230,13 @@ public final class QuotaController extends StateController {
private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
12 * 60 * 60 * 1000L; // 12 hours
+ // Latest default window size for EXEMPTED bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+ 40 * 60 * 1000L; // 40 minutes.
+ // Latest default window size for ACTIVE bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+ 60 * 60 * 1000L; // 60 minutes.
+
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3276,12 +3297,13 @@ public final class QuotaController extends StateController {
* bucket window.
*/
public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
/**
* How much time each app in the active bucket will have to run jobs within their standby
* bucket window.
*/
- public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
/**
* How much time each app in the working set bucket will have to run jobs within their
* standby bucket window.
@@ -3575,11 +3597,30 @@ public final class QuotaController extends StateController {
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
void adjustDefaultBucketWindowSizes() {
- WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+
mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
@@ -3592,6 +3633,11 @@ public final class QuotaController extends StateController {
mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
mAllowedTimePerPeriodMs[FREQUENT_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+ mAllowedTimePeriodAdditionaInstallerMs =
+ Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+ - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
void adjustDefaultEjLimits() {
@@ -3882,10 +3928,14 @@ public final class QuotaController extends StateController {
KEY_WINDOW_SIZE_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
ALLOWED_TIME_PER_PERIOD_WORKING_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3900,19 +3950,27 @@ public final class QuotaController extends StateController {
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+ : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
Flags.adjustQuotaDefaultConstants()
diff --git a/core/api/current.txt b/core/api/current.txt
index 1630d80346ce..4cd6d6f03ee8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46681,16 +46681,16 @@ package android.telephony {
method public int getLastCauseCode();
method @Nullable public android.net.LinkProperties getLinkProperties();
method public int getNetworkType();
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+ method public int getNetworkValidationStatus();
method public int getState();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
+ field public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
+ field public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
+ field public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
+ field public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
+ field public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
}
public final class RadioAccessSpecifier implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 132c65cc26ee..526a213a6003 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -410,7 +410,6 @@ package android.os {
method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 32b170a6286b..35720fd17769 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2941,6 +2941,7 @@ package android.app.smartspace.uitemplatedata {
package android.app.supervision {
@FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
+ method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
}
@@ -3642,11 +3643,26 @@ package android.companion.virtual.sensor {
method public int getDeviceId();
method @NonNull public String getName();
method public int getType();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo);
method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
}
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getType();
+ method @NonNull public java.util.List<float[]> getValues();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR;
+ }
+
+ public static final class VirtualSensorAdditionalInfo.Builder {
+ ctor public VirtualSensorAdditionalInfo.Builder(int);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build();
+ }
+
public interface VirtualSensorCallback {
method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
}
@@ -3664,6 +3680,7 @@ package android.companion.virtual.sensor {
method public float getResolution();
method public int getType();
method @Nullable public String getVendor();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
@@ -3672,6 +3689,7 @@ package android.companion.virtual.sensor {
public static final class VirtualSensorConfig.Builder {
ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaxDelay(int);
@@ -16442,7 +16460,7 @@ package android.telephony.data {
method @Deprecated public int getMtu();
method public int getMtuV4();
method public int getMtuV6();
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+ method public int getNetworkValidationStatus();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
method public int getPduSessionId();
method public int getProtocolType();
@@ -16479,7 +16497,7 @@ package android.telephony.data {
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -16559,7 +16577,7 @@ package android.telephony.data {
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16621,7 +16639,7 @@ package android.telephony.data {
method public final int getSlotIndex();
method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 12bfccf2172c..4c8283907712 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2460,7 +2460,7 @@ package android.os {
method public static void invalidateCache(@NonNull String, @NonNull String);
method public final boolean isDisabled();
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
+ method public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
field public static final String MODULE_SYSTEM = "system_server";
field public static final String MODULE_TEST = "test";
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index d5b2f980e1a6..9f789328d8e0 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -110,7 +110,7 @@ public class AnimationHandler {
}
};
- public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
+ public static final ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private static AnimationHandler sTestHandler = null;
private boolean mListDirty = false;
@@ -118,10 +118,12 @@ public class AnimationHandler {
if (sTestHandler != null) {
return sTestHandler;
}
- if (sAnimatorHandler.get() == null) {
- sAnimatorHandler.set(new AnimationHandler());
+ AnimationHandler animatorHandler = sAnimatorHandler.get();
+ if (animatorHandler == null) {
+ animatorHandler = new AnimationHandler();
+ sAnimatorHandler.set(animatorHandler);
}
- return sAnimatorHandler.get();
+ return animatorHandler;
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 54ab3b8f185b..62816a2fa0f5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3099,7 +3099,8 @@ public class ActivityManager {
/**
* Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
* activity along with the task, so it is positioned immediately behind
- * the task.
+ * the task. This flag is ignored if the task's windowing mode is
+ * {@link WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW}.
*/
public static final int MOVE_TASK_WITH_HOME = 0x00000001;
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index ea4646aa9eb9..3fd9d8b26611 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -104,6 +104,8 @@ public class AppCompatTaskInfo implements Parcelable {
public static final int FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE = FLAG_BASE << 9;
/** Top activity flag for whether restart menu is shown due to display move. */
private static final int FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE = FLAG_BASE << 10;
+ /** Top activity flag for whether activity opted out of edge to edge. */
+ public static final int FLAG_OPT_OUT_EDGE_TO_EDGE = FLAG_BASE << 11;
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
@@ -118,7 +120,8 @@ public class AppCompatTaskInfo implements Parcelable {
FLAG_FULLSCREEN_OVERRIDE_SYSTEM,
FLAG_FULLSCREEN_OVERRIDE_USER,
FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE,
- FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE
+ FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE,
+ FLAG_OPT_OUT_EDGE_TO_EDGE
})
public @interface TopActivityFlag {}
@@ -132,7 +135,8 @@ public class AppCompatTaskInfo implements Parcelable {
@TopActivityFlag
private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP
| FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM
- | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE;
+ | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE
+ | FLAG_OPT_OUT_EDGE_TO_EDGE;
@TopActivityFlag
private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED
@@ -347,6 +351,20 @@ public class AppCompatTaskInfo implements Parcelable {
setTopActivityFlag(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, enable);
}
+ /**
+ * Sets the top activity flag for whether the activity has opted out of edge to edge.
+ */
+ public void setOptOutEdgeToEdge(boolean enable) {
+ setTopActivityFlag(FLAG_OPT_OUT_EDGE_TO_EDGE, enable);
+ }
+
+ /**
+ * @return {@code true} if the top activity has opted out of edge to edge.
+ */
+ public boolean hasOptOutEdgeToEdge() {
+ return isTopActivityFlagEnabled(FLAG_OPT_OUT_EDGE_TO_EDGE);
+ }
+
/** Clear all top activity flags and set to false. */
public void clearTopActivityFlags() {
mTopActivityFlags = FLAG_UNDEFINED;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1ed64f9416c0..00fa1c1a4f80 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -200,6 +200,8 @@ public class ApplicationPackageManager extends PackageManager {
@GuardedBy("mPackageMonitorCallbacks")
private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
+ private final boolean mUseSystemFeaturesCache;
+
UserManager getUserManager() {
if (mUserManager == null) {
mUserManager = UserManager.get(mContext);
@@ -824,8 +826,7 @@ public class ApplicationPackageManager extends PackageManager {
if (maybeHasSystemFeature != null) {
return maybeHasSystemFeature;
}
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ if (mUseSystemFeaturesCache) {
maybeHasSystemFeature =
SystemFeaturesCache.getInstance().maybeHasFeature(name, version);
if (maybeHasSystemFeature != null) {
@@ -2221,6 +2222,25 @@ public class ApplicationPackageManager extends PackageManager {
protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
mContext = context;
mPM = pm;
+ mUseSystemFeaturesCache = isSystemFeaturesCacheEnabledAndAvailable();
+ }
+
+ private static boolean isSystemFeaturesCacheEnabledAndAvailable() {
+ if (!android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ return false;
+ }
+ if (!com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
+ return false;
+ }
+ if (ActivityThread.isSystem() && !SystemFeaturesCache.hasInstance()) {
+ // There are a handful of utility "system" processes that are neither system_server nor
+ // bound as applications. For these processes, we don't have access to application
+ // shared memory or the dependent system features cache.
+ // TODO(b/400713460): Revisit this exception after deprecating these command-like
+ // system processes.
+ return false;
+ }
+ return true;
}
/**
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 2daa52b47102..fa977c93113a 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@ public final class AutomaticZenRule implements Parcelable {
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == ENABLED;
if (source.readInt() == ENABLED) {
- name = getTrimmedString(source.readString8());
+ name = getTrimmedString(source.readString());
}
interruptionFilter = source.readInt();
conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@ public final class AutomaticZenRule implements Parcelable {
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null, ZenPolicy.class);
- mPkg = source.readString8();
+ mPkg = source.readString();
mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
- mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
+ mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
mType = source.readInt();
}
@@ -514,7 +514,7 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeInt(enabled ? ENABLED : DISABLED);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -524,11 +524,11 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
- dest.writeString8(mPkg);
+ dest.writeString(mPkg);
dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
- dest.writeString8(mTriggerDescription);
+ dest.writeString(mTriggerDescription);
dest.writeInt(mType);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e5c0fbe1ee1..99a2763650cd 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2473,11 +2473,9 @@ class ContextImpl extends Context {
@Override
public int getPermissionRequestState(String permission) {
Objects.requireNonNull(permission, "Permission name can't be null");
- int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
- permission);
PermissionManager permissionManager = getSystemService(PermissionManager.class);
return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
- deviceId);
+ getDeviceId());
}
@Override
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index feaa98f644a0..7c293cb9cb3b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6470,9 +6470,11 @@ public class Notification implements Parcelable
contentView.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
contentView.setTextViewText(R.id.notification_material_reply_text_3, null);
- // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
- contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
- RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+ if (!notificationsRedesignTemplates()) {
+ // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
+ contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+ RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+ }
}
private boolean bindSnoozeAction(RemoteViews contentView, StandardTemplateParams p) {
@@ -6489,7 +6491,7 @@ public class Notification implements Parcelable
final boolean snoozeEnabled = !hideSnoozeButton
&& mContext.getContentResolver() != null
&& isSnoozeSettingEnabled();
- if (snoozeEnabled) {
+ if (!notificationsRedesignTemplates() && snoozeEnabled) {
contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, 0);
}
@@ -6569,44 +6571,18 @@ public class Notification implements Parcelable
}
boolean validRemoteInput = false;
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child GONE instead.
+ int actionsContainerForVisibilityChange = notificationsRedesignTemplates()
+ ? R.id.actions_container_layout : R.id.actions_container;
if (numActions > 0 && !p.mHideActions) {
- contentView.setViewVisibility(R.id.actions_container, View.VISIBLE);
+ contentView.setViewVisibility(actionsContainerForVisibilityChange, View.VISIBLE);
contentView.setViewVisibility(R.id.actions, View.VISIBLE);
- contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
- RemoteViews.MARGIN_BOTTOM, 0);
- if (notificationsRedesignTemplates()) {
- // No need for additional space under smart replies/smart actions.
- contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
- RemoteViews.MARGIN_BOTTOM, 0);
- if (emphasizedMode) {
- // Emphasized actions look similar to smart replies, so let's use the same
- // margins.
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_TOP,
- R.dimen.notification_2025_smart_reply_container_margin);
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_smart_reply_container_margin);
- } else {
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_TOP, 0);
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_action_list_margin_bottom);
- }
- }
+ updateMarginsForActions(contentView, emphasizedMode);
validRemoteInput = populateActionsContainer(contentView, p, nonContextualActions,
numActions, emphasizedMode);
} else {
- contentView.setViewVisibility(R.id.actions_container, View.GONE);
- if (notificationsRedesignTemplates() && !snoozeEnabled) {
- // Make sure smart replies & smart actions have enough space at the bottom
- // (if present) when there are no actions. This should be set to 0 if we're
- // showing the snooze or bubble buttons.
- contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_smart_reply_container_margin);
- }
+ contentView.setViewVisibility(actionsContainerForVisibilityChange, View.GONE);
}
RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
@@ -6652,6 +6628,30 @@ public class Notification implements Parcelable
return contentView;
}
+ private void updateMarginsForActions(RemoteViews contentView, boolean emphasizedMode) {
+ if (notificationsRedesignTemplates()) {
+ if (emphasizedMode) {
+ // Emphasized actions look similar to smart replies, so let's use the same
+ // margins.
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_TOP,
+ R.dimen.notification_2025_smart_reply_container_margin);
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_BOTTOM,
+ R.dimen.notification_2025_smart_reply_container_margin);
+ } else {
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_TOP, 0);
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_BOTTOM,
+ R.dimen.notification_2025_action_list_margin_bottom);
+ }
+ } else {
+ contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+ RemoteViews.MARGIN_BOTTOM, 0);
+ }
+ }
+
private boolean populateActionsContainer(RemoteViews contentView, StandardTemplateParams p,
List<Action> nonContextualActions, int numActions, boolean emphasizedMode) {
boolean validRemoteInput = false;
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6e495768bfd4..38141cf20ce3 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1417,36 +1417,7 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Throw if the current process is not allowed to use test APIs.
- */
- @android.ravenwood.annotation.RavenwoodReplace
- private static void throwIfNotTest() {
- final ActivityThread activityThread = ActivityThread.currentActivityThread();
- if (activityThread == null) {
- // Only tests can reach here.
- return;
- }
- final Instrumentation instrumentation = activityThread.getInstrumentation();
- if (instrumentation == null) {
- // Only tests can reach here.
- return;
- }
- if (instrumentation.isInstrumenting()) {
- return;
- }
- if (Flags.enforcePicTestmodeProtocol()) {
- throw new IllegalStateException("Test-only API called not from a test.");
- }
- }
-
- /**
- * Do not throw if running under ravenwood.
- */
- private static void throwIfNotTest$ravenwood() {
- }
-
- /**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -1454,12 +1425,10 @@ public class PropertyInvalidatedCache<Query, Result> {
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public static void setTestMode(boolean mode) {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == mode) {
final String msg = "cannot set test mode redundantly: mode=" + mode;
@@ -1495,11 +1464,9 @@ public class PropertyInvalidatedCache<Query, Result> {
* for which it would not otherwise have permission. Caches in test mode do NOT write their
* values to the system properties. The effect is local to the current process. Test mode
* must be true when this method is called.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
public void testPropertyName() {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == false) {
throw new IllegalStateException("cannot test property name with test mode off");
@@ -1810,12 +1777,10 @@ public class PropertyInvalidatedCache<Query, Result> {
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public void disableSystemWide() {
- throwIfNotTest();
disableSystemWide(mPropertyName);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c74bd1a092ee..c528db8f1809 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -224,7 +224,7 @@ import java.util.function.Consumer;
* <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
* {@link UserManager#isSystemUser System User} or Main User, is
* the most powerful type of Device Policy Controller and can affect policy across the device.
- * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
+ * <li>A <i id="profileowner">Profile Owner</i>, which can exist on any user, can
* affect policy on the user it is on, and when it is running on
* {@link UserManager#isProfile a profile} has
* <a href="#profile-on-parent">limited</a> ability to affect policy on its parent.
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
index 83abc048af8a..e05ede580d3f 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -31,6 +31,7 @@ import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.GlobalSearchSession;
import android.app.appsearch.JoinSpec;
+import android.app.appsearch.PropertyPath;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchSpec;
@@ -141,6 +142,9 @@ public class AppFunctionManagerHelper {
.addFilterSchemas(
AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
targetPackage))
+ .addProjectionPaths(
+ SearchSpec.SCHEMA_TYPE_WILDCARD,
+ List.of(new PropertyPath(STATIC_PROPERTY_ENABLED_BY_DEFAULT)))
.setJoinSpec(joinSpec)
.setVerbatimSearchEnabled(true)
.build();
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7718d159896e..3c8b1a0482ad 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -366,7 +366,7 @@ public class JankDataProcessor {
30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
Integer.MAX_VALUE
};
- private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length];
+ private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length - 1];
// Histogram of frame duration overruns encoded in predetermined buckets.
public PendingJankStat() {
@@ -511,7 +511,7 @@ public class JankDataProcessor {
if (overrunTime <= 1000) {
return (overrunTime - 200) / 100 + 43;
}
- return sFrameOverrunHistogramBounds.length - 1;
+ return mFrameOverrunBuckets.length - 1;
}
}
diff --git a/core/java/android/app/jank/TEST_MAPPING b/core/java/android/app/jank/TEST_MAPPING
new file mode 100644
index 000000000000..271eb4332f79
--- /dev/null
+++ b/core/java/android/app/jank/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "postsubmit": [
+ {
+ "name": "CoreAppJankTestCases"
+ },
+ {
+ "name": "CtsAppJankTestCases"
+ }
+ ]
+} \ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 3eaf2c40daca..6f0eafe487af 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -269,7 +269,7 @@ flag {
namespace: "systemui"
description: "enables metrics when redacting notifications on the lockscreen"
bug: "343631648"
- metadata {
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
@@ -279,7 +279,16 @@ flag {
namespace: "systemui"
description: "enables user expanding the public view of a notification"
bug: "398853084"
- metadata {
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ }
+flag {
+ name: "notif_entry_creation_time_use_elapsed_realtime"
+ namespace: "systemui"
+ description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
+ bug: "343631648"
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
@@ -367,6 +376,7 @@ flag {
namespace: "systemui"
description: "Allows the NAS to summarize notifications"
bug: "390417189"
+ is_exported: true
}
flag {
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8217ca1953ab..172ed2358a5d 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -97,6 +97,8 @@ public class SupervisionManager {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SUPERVISION_MANAGER_APIS)
@RequiresPermission(anyOf = {MANAGE_USERS, QUERY_USERS})
@Nullable
public Intent createConfirmSupervisionCredentialsIntent() {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f8ac27de1754..db77adeb5a3d 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtual.camera.VirtualCameraConfig;
@@ -251,6 +252,11 @@ interface IVirtualDevice {
boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
/**
+ * Sends additional information about the virtual sensor corresponding to the given token.
+ */
+ boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info);
+
+ /**
* Launches a pending intent on the given display that is owned by this virtual device.
*/
void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 4fb3982c3754..615a6dffdf99 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -158,3 +158,11 @@ flag {
bug: "370720522"
is_exported: true
}
+
+flag {
+ name: "virtual_sensor_additional_info"
+ namespace: "virtual_devices"
+ description: "API for injecting SensorAdditionalInfo for VirtualSensor"
+ bug: "393517834"
+ is_exported: true
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 934a1a8ffcbd..8d4acfcb30d7 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -16,12 +16,15 @@
package android.companion.virtual.sensor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,20 +40,33 @@ import android.os.RemoteException;
*/
@SystemApi
public final class VirtualSensor implements Parcelable {
+
private final int mHandle;
private final int mType;
private final String mName;
+ private final int mFlags;
private final IVirtualDevice mVirtualDevice;
private final IBinder mToken;
+ // Only one additional info frame set at a time.
+ private final Object mAdditionalInfoLock = new Object();
/**
* @hide
*/
public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
IBinder token) {
+ this(handle, type, name, /*flags=*/0, virtualDevice, token);
+ }
+
+ /**
+ * @hide
+ */
+ public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice,
+ IBinder token) {
mHandle = handle;
mType = type;
mName = name;
+ mFlags = flags;
mVirtualDevice = virtualDevice;
mToken = token;
}
@@ -61,13 +77,14 @@ public final class VirtualSensor implements Parcelable {
@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
@TestApi
public VirtualSensor(int handle, int type, @NonNull String name) {
- this(handle, type, name, /*virtualDevice=*/null, /*token=*/null);
+ this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null);
}
private VirtualSensor(Parcel parcel) {
mHandle = parcel.readInt();
mType = parcel.readInt();
mName = parcel.readString8();
+ mFlags = parcel.readInt();
mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
mToken = parcel.readStrongBinder();
}
@@ -123,6 +140,7 @@ public final class VirtualSensor implements Parcelable {
parcel.writeInt(mHandle);
parcel.writeInt(mType);
parcel.writeString8(mName);
+ parcel.writeInt(mFlags);
parcel.writeStrongBinder(mVirtualDevice.asBinder());
parcel.writeStrongBinder(mToken);
}
@@ -143,6 +161,33 @@ public final class VirtualSensor implements Parcelable {
}
}
+ /**
+ * Send additional information about the sensor to the system.
+ *
+ * @param info the additional sensor information to send.
+ * @throws UnsupportedOperationException if the sensor does not support sending additional info.
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
+ if (!Flags.virtualSensorAdditionalInfo()) {
+ return;
+ }
+ if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
+ throw new UnsupportedOperationException("Sensor additional info not supported.");
+ }
+ try {
+ synchronized (mAdditionalInfoLock) {
+ mVirtualDevice.sendSensorAdditionalInfo(mToken, info);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
public static final Parcelable.Creator<VirtualSensor> CREATOR =
new Parcelable.Creator<VirtualSensor>() {
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
new file mode 100644
index 000000000000..7267be88ca75
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.sensor;
+
+parcelable VirtualSensorAdditionalInfo; \ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
new file mode 100644
index 000000000000..a4fca507b1d5
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.sensor;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.hardware.SensorAdditionalInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An additional information frame for a {@link VirtualSensor}, which is reported through listener
+ * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}.
+ *
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+@SystemApi
+public final class VirtualSensorAdditionalInfo implements Parcelable {
+
+ private final int mType;
+ @NonNull
+ private final List<float[]> mValues;
+
+ /** @hide */
+ @IntDef(prefix = "TYPE_", value = {
+ SensorAdditionalInfo.TYPE_UNTRACKED_DELAY,
+ SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE,
+ SensorAdditionalInfo.TYPE_VEC3_CALIBRATION,
+ SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT,
+ SensorAdditionalInfo.TYPE_SAMPLING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) {
+ mType = type;
+ mValues = values;
+ }
+
+ private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) {
+ mType = parcel.readInt();
+ final int valuesLength = parcel.readInt();
+ mValues = new ArrayList<>(valuesLength);
+ for (int i = 0; i < valuesLength; ++i) {
+ mValues.add(parcel.createFloatArray());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mValues.size());
+ for (int i = 0; i < mValues.size(); ++i) {
+ parcel.writeFloatArray(mValues.get(i));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the type of this information frame.
+ *
+ * @see SensorAdditionalInfo#type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the float values of this information frame, if any.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public List<float[]> getValues() {
+ return mValues;
+ }
+
+ /**
+ * Builder for {@link VirtualSensorAdditionalInfo}.
+ */
+ public static final class Builder {
+
+ @VirtualSensorAdditionalInfo.Type
+ private final int mType;
+ @NonNull
+ private final ArrayList<float[]> mValues = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ *
+ * @param type type of this additional info frame.
+ * @see SensorAdditionalInfo
+ */
+ public Builder(@VirtualSensorAdditionalInfo.Type int type) {
+ switch (type) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ mType = type;
+ }
+
+ /**
+ * Additional info payload data represented in float values. Depending on the type of
+ * information, this may be null.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public Builder addValues(@NonNull float[] values) {
+ if (values.length > 14) {
+ throw new IllegalArgumentException("Maximum payload value size is 14.");
+ }
+ if (mValues.isEmpty()) {
+ switch (mType) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ assertValuesLength(values, 2);
+ break;
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ assertValuesLength(values, 1);
+ break;
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ assertValuesLength(values, 11);
+ break;
+ }
+ } else if (values.length != mValues.getFirst().length) {
+ throw new IllegalArgumentException("All payload values must have the same length");
+ }
+
+ mValues.add(values);
+ return this;
+ }
+
+ private void assertValuesLength(float[] values, int expected) {
+ if (values.length != expected) {
+ throw new IllegalArgumentException(
+ "Payload values must have size " + expected + " for type " + mType);
+ }
+ }
+
+ /**
+ * Creates a new {@link VirtualSensorAdditionalInfo}.
+ *
+ * @throws IllegalArgumentException if the payload doesn't match the expectation for the
+ * given type, as documented in {@link SensorAdditionalInfo}.
+ */
+ @NonNull
+ public VirtualSensorAdditionalInfo build() {
+ if (mValues.isEmpty()) {
+ throw new IllegalArgumentException("Payload is required");
+ }
+ return new VirtualSensorAdditionalInfo(mType, mValues);
+ }
+ }
+
+ public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR =
+ new Creator<>() {
+ public VirtualSensorAdditionalInfo createFromParcel(Parcel source) {
+ return new VirtualSensorAdditionalInfo(source);
+ }
+
+ public VirtualSensorAdditionalInfo[] newArray(int size) {
+ return new VirtualSensorAdditionalInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 68bc9bce28d2..be8974ec29ad 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -59,10 +59,14 @@ public final class VirtualSensorConfig implements Parcelable {
private static final int REPORTING_MODE_MASK = 0xE;
private static final int REPORTING_MODE_SHIFT = 1;
+ // Mask for indication bit of sensor additional information support, bit 6.
+ static final int ADDITIONAL_INFO_MASK = 0x40;
+
// Mask for direct mode highest rate level, bit 7, 8, 9.
private static final int DIRECT_REPORT_MASK = 0x380;
private static final int DIRECT_REPORT_SHIFT = 7;
+
// Mask for supported direct channel, bit 10, 11
private static final int DIRECT_CHANNEL_SHIFT = 10;
@@ -253,6 +257,18 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Returns whether the sensor supports additional information.
+ *
+ * @see Builder#setAdditionalInfoSupported(boolean)
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public boolean isAdditionalInfoSupported() {
+ return (mFlags & ADDITIONAL_INFO_MASK) > 0;
+ }
+
+ /**
* Returns the reporting mode of this sensor.
*
* @see Builder#setReportingMode(int)
@@ -450,6 +466,25 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Sets whether this sensor supports sensor additional information.
+ *
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ @NonNull
+ public VirtualSensorConfig.Builder setAdditionalInfoSupported(
+ boolean additionalInfoSupported) {
+ if (additionalInfoSupported) {
+ mFlags |= ADDITIONAL_INFO_MASK;
+ } else {
+ mFlags &= ~ADDITIONAL_INFO_MASK;
+ }
+ return this;
+ }
+
+ /**
* Sets the reporting mode of this sensor.
*
* @throws IllegalArgumentException if the reporting mode is not one of
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 038756148a32..bb62ac321202 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12426,6 +12426,8 @@ public class Intent implements Parcelable, Cloneable {
}
private void collectNestedIntentKeysRecur(Set<Intent> visited, boolean forceUnparcel) {
+ // if forceUnparcel is false, do not unparcel the mExtras bundle.
+ // forceUnparcel will only be true when this method is called from system server.
if (mExtras != null && (forceUnparcel || !mExtras.isParcelled()) && !mExtras.isEmpty()) {
addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
for (String key : mExtras.keySet()) {
@@ -12440,6 +12442,7 @@ public class Intent implements Parcelable, Cloneable {
value = mExtras.get(key);
} else {
value = null;
+ removeExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
}
} catch (BadParcelableException e) {
// This may still happen if the keys are collected on the system server side, in
@@ -12459,6 +12462,13 @@ public class Intent implements Parcelable, Cloneable {
}
}
+ // if there is no extras in the bundle, we also mark the intent as keys are collected.
+ // isDefinitelyEmpty() will not unparceled the mExtras. This is the best we can do without
+ // unparceling the extra bundle.
+ if (mExtras == null || mExtras.isDefinitelyEmpty()) {
+ addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+ }
+
if (mClipData != null) {
for (int i = 0; i < mClipData.getItemCount(); i++) {
Intent intent = mClipData.getItemAt(i).mIntent;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 10d3051cff6f..ded35b23608d 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -31,13 +31,13 @@ import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -98,15 +98,16 @@ public abstract class RegisteredServicesCache<V> {
@GuardedBy("mServicesLock")
private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
- @GuardedBy("mServiceInfoCaches")
- private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+ @GuardedBy("mUserIdToServiceInfoCaches")
+ private final SparseArrayMap<ComponentName, ServiceInfo<V>> mUserIdToServiceInfoCaches =
+ new SparseArrayMap<>();
private final Handler mBackgroundHandler;
private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
public void run() {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.clear();
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.clear();
}
}
};
@@ -537,8 +538,8 @@ public abstract class RegisteredServicesCache<V> {
Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
}
if (lastUpdateTime >= 0) {
- ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
- lastUpdateTime);
+ ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(userId,
+ componentName, lastUpdateTime);
if (serviceInfo != null) {
serviceInfos.add(serviceInfo);
continue;
@@ -553,8 +554,8 @@ public abstract class RegisteredServicesCache<V> {
}
serviceInfos.add(info);
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.put(componentName, info);
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.add(userId, componentName, info);
}
}
} catch (XmlPullParserException | IOException e) {
@@ -563,8 +564,8 @@ public abstract class RegisteredServicesCache<V> {
}
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- if (!mServiceInfoCaches.isEmpty()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ if (mUserIdToServiceInfoCaches.numMaps() > 0) {
mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
@@ -873,6 +874,11 @@ public abstract class RegisteredServicesCache<V> {
synchronized (mServicesLock) {
mUserServices.remove(userId);
}
+ if (Flags.optimizeParsingInRegisteredServicesCache()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.delete(userId);
+ }
+ }
}
@VisibleForTesting
@@ -916,10 +922,10 @@ public abstract class RegisteredServicesCache<V> {
mContext.unregisterReceiver(mUserRemovedReceiver);
}
- private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName,
- long lastUpdateTime) {
- synchronized (mServiceInfoCaches) {
- ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName);
+ private ServiceInfo<V> getServiceInfoFromServiceCache(int userId,
+ @NonNull ComponentName componentName, long lastUpdateTime) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ ServiceInfo<V> serviceCache = mUserIdToServiceInfoCaches.get(userId, componentName);
if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) {
return serviceCache;
}
diff --git a/core/java/android/content/pm/SystemFeaturesCache.java b/core/java/android/content/pm/SystemFeaturesCache.java
index b3d70fa8bfaf..57dd1e6f4d3b 100644
--- a/core/java/android/content/pm/SystemFeaturesCache.java
+++ b/core/java/android/content/pm/SystemFeaturesCache.java
@@ -74,6 +74,11 @@ public final class SystemFeaturesCache {
return instance;
}
+ /** Checks for existence of the process-global instance. */
+ public static boolean hasInstance() {
+ return sInstance != null;
+ }
+
/** Clears the process-global cache instance for testing. */
@VisibleForTesting
public static void clearInstance() {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 23f1ff8926df..92ae15a296b7 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -136,6 +136,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
@@ -261,6 +283,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 7f57f5dbf0ab..3411a4897e83 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -636,3 +636,13 @@ flag {
description: "Enables support for new supervising user type"
bug: "389712089"
}
+
+flag {
+ name: "use_unified_resources"
+ namespace: "multiuser"
+ description: "Use same resources"
+ bug: "392972139"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
index fdde2057a1a0..de3bafb4bb56 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -75,7 +75,6 @@ public class MarshalQueryableParcelable<T extends Parcelable>
}
Parcel parcel = Parcel.obtain();
- byte[] parcelContents;
try {
value.writeToParcel(parcel, /*flags*/0);
@@ -85,17 +84,15 @@ public class MarshalQueryableParcelable<T extends Parcelable>
"Parcelable " + value + " must not have file descriptors");
}
- parcelContents = parcel.marshall();
+ final int position = buffer.position();
+ parcel.marshall(buffer);
+ if (buffer.position() == position) {
+ throw new AssertionError("No data marshaled for " + value);
+ }
}
finally {
parcel.recycle();
}
-
- if (parcelContents.length == 0) {
- throw new AssertionError("No data marshaled for " + value);
- }
-
- buffer.put(parcelContents);
}
@Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 92a56fc575e3..8216130e3731 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -433,8 +433,9 @@ public final class DisplayManager {
public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
/**
- * Virtual display flag: Indicates that the display should support system decorations. Virtual
- * displays without this flag shouldn't show home, navigation bar or wallpaper.
+ * Virtual display flag: Indicates that the display supports and should always show system
+ * decorations. Virtual displays without this flag shouldn't show home, navigation bar or
+ * wallpaper.
* <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
*
* @see #createVirtualDisplay
diff --git a/core/java/android/hardware/serial/OWNERS b/core/java/android/hardware/serial/OWNERS
index bc2c66ae7ecd..13f6774a4264 100644
--- a/core/java/android/hardware/serial/OWNERS
+++ b/core/java/android/hardware/serial/OWNERS
@@ -3,4 +3,5 @@ mjel@google.com
chominskib@google.com
wzwonarz@google.com
gstepniewski@google.com
-xutan@google.com \ No newline at end of file
+xutan@google.com
+ovn@google.com
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 894b068b1528..2e7bc6d9b9f7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -428,6 +428,9 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@AnyThread
public static boolean canImeRenderGesturalNavButtons() {
+ if (Flags.disallowDisablingImeNavigationBar()) {
+ return true;
+ }
return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index e888f520b842..2e7c3be53d90 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -718,7 +718,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
}
/**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -726,11 +726,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
- @FlaggedApi(android.os.Flags.FLAG_IPC_DATA_CACHE_TEST_APIS)
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static void setTestMode(boolean mode) {
PropertyInvalidatedCache.setTestMode(mode);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 49d3f06eb80f..6cb49b3ea166 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -52,6 +52,8 @@ import com.android.internal.util.ArrayUtils;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
import libcore.util.SneakyThrow;
import java.io.ByteArrayInputStream;
@@ -62,6 +64,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.nio.ByteBuffer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
@@ -457,8 +460,15 @@ public final class Parcel {
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
+ private static native int nativeMarshallArray(
+ long nativePtr, byte[] data, int offset, int length);
+ private static native int nativeMarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
+ private static native void nativeUnmarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
+
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
private static native boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length);
@@ -814,12 +824,80 @@ public final class Parcel {
}
/**
+ * Writes the raw bytes of the parcel to a buffer.
+ *
+ * <p class="note">The data you retrieve here <strong>must not</strong>
+ * be placed in any kind of persistent storage (on local disk, across
+ * a network, etc). For that, you should use standard serialization
+ * or another kind of general serialization mechanism. The Parcel
+ * marshalled representation is highly optimized for local IPC, and as
+ * such does not attempt to maintain compatibility with data created
+ * in different versions of the platform.
+ *
+ * @param buffer The ByteBuffer to write the data to.
+ * @throws ReadOnlyBufferException if the buffer is read-only.
+ * @throws BufferOverflowException if the buffer is too small.
+ *
+ * @hide
+ */
+ public final void marshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+ if (buffer.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ int marshalledSize = 0;
+ if (buffer.isDirect()) {
+ marshalledSize = nativeMarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ marshalledSize = nativeMarshallArray(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + marshalledSize);
+ }
+
+ /**
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
nativeUnmarshall(mNativePtr, data, offset, length);
}
+ /**
+ * Fills the raw bytes of this Parcel with data from the supplied buffer.
+ *
+ * @param buffer will read buffer.remaining() bytes from the buffer.
+ *
+ * @hide
+ */
+ public final void unmarshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ if (buffer.isDirect()) {
+ nativeUnmarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ nativeUnmarshall(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + remaining);
+ }
+
public final void appendFrom(Parcel parcel, int offset, int length) {
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 5d80119410e1..86acb2b21cfa 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -227,14 +227,6 @@ flag {
}
flag {
- name: "ipc_data_cache_test_apis"
- namespace: "system_performance"
- description: "Expose IpcDataCache test apis to mainline modules."
- bug: "396173886"
- is_exported: true
-}
-
-flag {
name: "mainline_vcn_platform_api"
namespace: "vcn"
description: "Expose platform APIs to mainline VCN"
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 561a2c96e6a7..17033d1143c6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1907,8 +1907,9 @@ public final class PermissionManager {
@Context.PermissionRequestState
public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
int deviceId) {
+ int resolvedDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permission);
return sPermissionRequestStateCache.query(
- new PermissionRequestStateQuery(packageName, permission, deviceId));
+ new PermissionRequestStateQuery(packageName, permission, resolvedDeviceId));
}
/**
@@ -2035,7 +2036,8 @@ public final class PermissionManager {
*/
public int checkPackageNamePermission(String permName, String pkgName,
int deviceId, @UserIdInt int userId) {
- String persistentDeviceId = getPersistentDeviceId(deviceId);
+ int resolvedDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permName);
+ String persistentDeviceId = getPersistentDeviceId(resolvedDeviceId);
return sPackageNamePermissionCache.query(
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index da4709b4b8b1..85f38c984f5c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4184,7 +4184,6 @@ public final class Settings {
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_REDIRECT_URL);
MOVED_TO_SECURE.add(Secure.SETTINGS_CLASSNAME);
MOVED_TO_SECURE.add(Secure.USE_GOOGLE_MAIL);
- MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
MOVED_TO_SECURE.add(Secure.WIFI_NUM_OPEN_NETWORKS_KEPT);
MOVED_TO_SECURE.add(Secure.WIFI_ON);
@@ -4223,6 +4222,7 @@ public final class Settings {
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MAX_DHCP_RETRY_COUNT);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
// these are moving directly from system to global
MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
@@ -6484,6 +6484,14 @@ public final class Settings {
public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
/**
+ * Setting to enable CV (proprietary)
+ *
+ * @hide
+ */
+ public static final String CV_ENABLED =
+ "cv_enabled";
+
+ /**
* Integer property that specifes the color for screen flash notification as a
* packed 32-bit color.
*
@@ -12822,6 +12830,22 @@ public final class Settings {
public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
/**
+ * Whether the Adaptive wifi scorer switch is enabled.
+ *
+ * @hide
+ */
+ public static final String ADAPTIVE_CONNECTIVITY_WIFI_ENABLED =
+ "adaptive_connectivity_wifi_enabled";
+
+ /**
+ * Whether the Adaptive 5G PM switch is enabled.
+ *
+ * @hide
+ */
+ public static final String ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED =
+ "adaptive_connectivity_mobile_network_enabled";
+
+ /**
* Controls the 'Sunlight boost' toggle in wearable devices (high brightness mode).
*
* Valid values for this key are: '0' (disabled) or '1' (enabled).
@@ -15498,7 +15522,8 @@ public final class Settings {
* <ul>
* <li><pre>secure</pre>: creates a secure display</li>
* <li><pre>own_content_only</pre>: only shows this display's own content</li>
- * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+ * <li><pre>should_show_system_decorations</pre>: always shows system decorations</li>
+ * <li><pre>fixed_content_mode</pre>: does not allow the content mode switch</li>
* </ul>
* </p><p>
* Example:
@@ -20799,6 +20824,24 @@ public final class Settings {
@Readable
public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode";
+ /**
+ * Setting indicating whether the primary gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_PRIMARY_ACTION_USER_PREFERENCE =
+ "gesture_primary_action_user_preference";
+
+ /**
+ * Setting indicating whether the dismiss gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_DISMISS_ACTION_USER_PREFERENCE =
+ "gesture_dismiss_action_user_preference";
+
/** Whether Wear Power Anomaly Service is enabled.
*
* (0 = false, 1 = true)
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 0b2239aa42b2..62b2bcf32442 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -124,6 +124,18 @@ public final class AdvancedProtectionManager {
@Retention(RetentionPolicy.SOURCE)
public @interface FeatureId {}
+ /** @hide */
+ public static String featureIdToString(@FeatureId int featureId) {
+ return switch(featureId) {
+ case FEATURE_ID_DISALLOW_CELLULAR_2G -> "DISALLOW_CELLULAR_2G";
+ case FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES -> "DISALLOW_INSTALL_UNKNOWN_SOURCES";
+ case FEATURE_ID_DISALLOW_USB -> "DISALLOW_USB";
+ case FEATURE_ID_DISALLOW_WEP -> "DISALLOW_WEP";
+ case FEATURE_ID_ENABLE_MTE -> "ENABLE_MTE";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
FEATURE_ID_DISALLOW_CELLULAR_2G,
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
@@ -147,7 +159,7 @@ public final class AdvancedProtectionManager {
"android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
/**
- * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+ * An int extra used with {@link #createSupportIntent} to identify the feature that needs to
* show a support dialog explaining it was disabled by advanced protection.
*
* @hide */
@@ -156,7 +168,7 @@ public final class AdvancedProtectionManager {
"android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
/**
- * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+ * An int extra used with {@link #createSupportIntent} to identify the type of the action that
* needs to be explained in the support dialog.
*
* @hide */
@@ -194,6 +206,16 @@ public final class AdvancedProtectionManager {
@Retention(RetentionPolicy.SOURCE)
public @interface SupportDialogType {}
+ /** @hide */
+ public static String supportDialogTypeToString(@SupportDialogType int type) {
+ return switch(type) {
+ case SUPPORT_DIALOG_TYPE_UNKNOWN -> "UNKNOWN";
+ case SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION -> "BLOCKED_INTERACTION";
+ case SUPPORT_DIALOG_TYPE_DISABLED_SETTING -> "DISABLED_SETTING";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
SUPPORT_DIALOG_TYPE_UNKNOWN,
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
@@ -372,6 +394,17 @@ public final class AdvancedProtectionManager {
return createSupportIntent(featureId, type);
}
+ /** @hide */
+ @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ try {
+ mService.logDialogShown(featureId, type, learnMoreClicked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* A callback class for monitoring changes to Advanced Protection state
*
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index 1939f829c700..0fc0fbea2989 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -35,4 +35,6 @@ interface IAdvancedProtectionService {
void setAdvancedProtectionEnabled(boolean enabled);
@EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
+ @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
+ void logDialogShown(int featureId, int type, boolean learnMoreClicked);
} \ No newline at end of file
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index ce31e1ea7e38..df3b8baa40c8 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -455,7 +455,7 @@ public class DreamService extends Service implements Window.Callback {
// Simply wake up in the case the device is not locked.
if (!keyguardManager.isKeyguardLocked()) {
- wakeUp();
+ wakeUp(false);
return true;
}
@@ -477,11 +477,11 @@ public class DreamService extends Service implements Window.Callback {
if (!mInteractive) {
if (mDebug) Slog.v(mTag, "Waking up on keyEvent");
- wakeUp();
+ wakeUp(false);
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mDebug) Slog.v(mTag, "Waking up on back key");
- wakeUp();
+ wakeUp(false);
return true;
}
return mWindow.superDispatchKeyEvent(event);
@@ -492,7 +492,7 @@ public class DreamService extends Service implements Window.Callback {
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent");
- wakeUp();
+ wakeUp(false);
return true;
}
return mWindow.superDispatchKeyShortcutEvent(event);
@@ -505,7 +505,7 @@ public class DreamService extends Service implements Window.Callback {
// but finish()es on any other kind of activity
if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
if (mDebug) Slog.v(mTag, "Waking up on touchEvent");
- wakeUp();
+ wakeUp(false);
return true;
}
return mWindow.superDispatchTouchEvent(event);
@@ -516,7 +516,7 @@ public class DreamService extends Service implements Window.Callback {
public boolean dispatchTrackballEvent(MotionEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(mTag, "Waking up on trackballEvent");
- wakeUp();
+ wakeUp(false);
return true;
}
return mWindow.superDispatchTrackballEvent(event);
@@ -527,7 +527,7 @@ public class DreamService extends Service implements Window.Callback {
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent");
- wakeUp();
+ wakeUp(false);
return true;
}
return mWindow.superDispatchGenericMotionEvent(event);
@@ -925,32 +925,37 @@ public class DreamService extends Service implements Window.Callback {
}
}
- private synchronized void updateDoze() {
- if (mDreamToken == null) {
- Slog.w(mTag, "Updating doze without a dream token.");
- return;
- }
+ /**
+ * Updates doze state. Note that this must be called on the mHandler.
+ */
+ private void updateDoze() {
+ mHandler.post(() -> {
+ if (mDreamToken == null) {
+ Slog.w(mTag, "Updating doze without a dream token.");
+ return;
+ }
- if (mDozing) {
- try {
- Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
- + " mDozeScreenBrightness=" + mDozeScreenBrightness
- + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
- if (startAndStopDozingInBackground()) {
- mDreamManager.startDozingOneway(
- mDreamToken, mDozeScreenState, mDozeScreenStateReason,
- mDozeScreenBrightnessFloat, mDozeScreenBrightness,
- mUseNormalBrightnessForDoze);
- } else {
- mDreamManager.startDozing(
- mDreamToken, mDozeScreenState, mDozeScreenStateReason,
- mDozeScreenBrightnessFloat, mDozeScreenBrightness,
- mUseNormalBrightnessForDoze);
+ if (mDozing) {
+ try {
+ Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
+ + " mDozeScreenBrightness=" + mDozeScreenBrightness
+ + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
+ if (startAndStopDozingInBackground()) {
+ mDreamManager.startDozingOneway(
+ mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+ mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+ mUseNormalBrightnessForDoze);
+ } else {
+ mDreamManager.startDozing(
+ mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+ mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+ mUseNormalBrightnessForDoze);
+ }
+ } catch (RemoteException ex) {
+ // system server died
}
- } catch (RemoteException ex) {
- // system server died
}
- }
+ });
}
/**
@@ -966,14 +971,20 @@ public class DreamService extends Service implements Window.Callback {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void stopDozing() {
- if (mDozing) {
- mDozing = false;
- try {
- mDreamManager.stopDozing(mDreamToken);
- } catch (RemoteException ex) {
- // system server died
+ mHandler.post(() -> {
+ if (mDreamToken == null) {
+ return;
}
- }
+
+ if (mDozing) {
+ mDozing = false;
+ try {
+ mDreamManager.stopDozing(mDreamToken);
+ } catch (RemoteException ex) {
+ // system server died
+ }
+ }
+ });
}
/**
@@ -1201,7 +1212,7 @@ public class DreamService extends Service implements Window.Callback {
@Override
public void onExitRequested() {
// Simply finish dream when exit is requested.
- mHandler.post(() -> finish());
+ mHandler.post(() -> finishInternal());
}
@Override
@@ -1299,9 +1310,13 @@ public class DreamService extends Service implements Window.Callback {
* </p>
*/
public final void finish() {
+ mHandler.post(this::finishInternal);
+ }
+
+ private void finishInternal() {
// If there is an active overlay connection, signal that the dream is ending before
- // continuing. Note that the overlay cannot rely on the unbound state, since another dream
- // might have bound to it in the meantime.
+ // continuing. Note that the overlay cannot rely on the unbound state, since another
+ // dream might have bound to it in the meantime.
if (mOverlayConnection != null) {
mOverlayConnection.addConsumer(overlay -> {
try {
@@ -1357,7 +1372,7 @@ public class DreamService extends Service implements Window.Callback {
* </p>
*/
public final void wakeUp() {
- wakeUp(false);
+ mHandler.post(()-> wakeUp(false));
}
/**
@@ -1559,7 +1574,7 @@ public class DreamService extends Service implements Window.Callback {
if (mActivity != null && !mActivity.isFinishing()) {
mActivity.finishAndRemoveTask();
} else {
- finish();
+ finishInternal();
}
mDreamToken = null;
@@ -1719,7 +1734,7 @@ public class DreamService extends Service implements Window.Callback {
// the window reference in order to fully release the DreamActivity.
mWindow = null;
mActivity = null;
- finish();
+ finishInternal();
}
if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 7660ed96d30e..815444d195c9 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -219,7 +219,7 @@ public class NotificationRankingUpdate implements Parcelable {
// Gets a read/write buffer mapping the entire shared memory region.
buffer = mRankingMapFd.mapReadWrite();
// Puts the ranking map into the shared memory region buffer.
- buffer.put(mapParcel.marshall(), 0, mapSize);
+ mapParcel.marshall(buffer);
// Protects the region from being written to, by setting it to be read-only.
mRankingMapFd.setProtect(OsConstants.PROT_READ);
// Puts the SharedMemory object in the parcel.
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index dc7129cde5e5..ea7ee4addbe6 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1cf43d455be8..4cbd5beb3a8c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2636,7 +2636,7 @@ public class ZenModeConfig implements Parcelable {
enabled = source.readInt() == 1;
snoozing = source.readInt() == 1;
if (source.readInt() == 1) {
- name = source.readString8();
+ name = source.readString();
}
zenMode = source.readInt();
conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2644,18 @@ public class ZenModeConfig implements Parcelable {
component = source.readParcelable(null, android.content.ComponentName.class);
configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
if (source.readInt() == 1) {
- id = source.readString8();
+ id = source.readString();
}
creationTime = source.readLong();
if (source.readInt() == 1) {
- enabler = source.readString8();
+ enabler = source.readString();
}
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
- pkg = source.readString8();
+ pkg = source.readString();
allowManualInvocation = source.readBoolean();
- iconResName = source.readString8();
- triggerDescription = source.readString8();
+ iconResName = source.readString();
+ triggerDescription = source.readString();
type = source.readInt();
userModifiedFields = source.readInt();
zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2703,7 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(snoozing ? 1 : 0);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -2714,23 +2714,23 @@ public class ZenModeConfig implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
if (id != null) {
dest.writeInt(1);
- dest.writeString8(id);
+ dest.writeString(id);
} else {
dest.writeInt(0);
}
dest.writeLong(creationTime);
if (enabler != null) {
dest.writeInt(1);
- dest.writeString8(enabler);
+ dest.writeString(enabler);
} else {
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
dest.writeParcelable(zenDeviceEffects, 0);
- dest.writeString8(pkg);
+ dest.writeString(pkg);
dest.writeBoolean(allowManualInvocation);
- dest.writeString8(iconResName);
- dest.writeString8(triggerDescription);
+ dest.writeString(iconResName);
+ dest.writeString(triggerDescription);
dest.writeInt(type);
dest.writeInt(userModifiedFields);
dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 6ed8c6d195e6..929e39f8b65c 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -421,7 +421,12 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
Intent intent = new Intent(SERVICE_INTERFACE);
intent.setComponent(serviceInfo.getComponentName());
int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
- mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+ if (mServiceInfo == null) {
+ mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+ } else {
+ mLifecycleExecutor.execute(() -> mContext.bindServiceAsUser(intent, this, flags,
+ UserHandle.of(mServiceInfo.getUserId())));
+ }
resetServiceConnectionTimeout();
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3f45e29f2d43..5c816543e191 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -286,7 +286,7 @@ public final class Display {
/**
* Display flag: Indicates that the display should show system decorations.
* <p>
- * This flag identifies secondary displays that should show system decorations, such as
+ * This flag identifies secondary displays that should always show system decorations, such as
* navigation bar, home activity or wallpaper.
* </p>
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
@@ -401,6 +401,18 @@ public final class Display {
public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 14;
/**
+ * Display flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag shouldn't be enabled with {@link #FLAG_PRIVATE} or
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} at the same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 15;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d880072aa404..bf000d5fa39a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -1125,6 +1125,9 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_REAR) != 0) {
result.append(", FLAG_REAR_DISPLAY");
}
+ if ((flags & Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0) {
+ result.append(", FLAG_ALLOWS_CONTENT_MODE_SWITCH");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4fc894ca9ff4..421d28ddd431 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -699,7 +699,7 @@ interface IWindowManager
/**
* Indicates the display should show system decors.
* <p>
- * System decors include status bar, navigation bar, launcher.
+ * System decors include status bar, navigation bar, launcher, and wallpaper.
* </p>
*
* @param displayId The id of the display.
@@ -719,6 +719,23 @@ interface IWindowManager
void setShouldShowSystemDecors(int displayId, boolean shouldShow);
/**
+ * Indicates that the display is eligible for the desktop mode from WindowManager's perspective.
+ * This includes:
+ * - The default display;
+ * - Any display that is allowed to switch the content mode between extended and mirroring
+ * (which means it can dynamically add or remove system decors), and it is now in extended mode
+ * (should currently show system decors).
+ * <p>
+ * System decors include status bar, navigation bar, launcher, and wallpaper.
+ * </p>
+ *
+ * @param displayId The id of the display.
+ * @return {@code true} if the display is eligible for the desktop mode from WindowManager's
+ * perspective.
+ */
+ boolean isEligibleForDesktopMode(int displayId);
+
+ /**
* Indicates the policy for how the display should show IME.
*
* @param displayId The id of the display.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7dc96f21b5ae..2edce5de7ace 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -555,8 +555,6 @@ public final class ViewRootImpl implements ViewParent,
@UiContext
public final Context mContext;
- private UiModeManager mUiModeManager;
-
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -2079,8 +2077,7 @@ public final class ViewRootImpl implements ViewParent,
// We also ignore dark theme, since the app developer can override the user's
// preference for dark mode in configuration.uiMode. Instead, we assume that both
// force invert and the system's dark theme are enabled.
- if (getUiModeManager().getForceInvertState() ==
- UiModeManager.FORCE_INVERT_TYPE_DARK) {
+ if (shouldApplyForceInvertDark()) {
final boolean isLightTheme =
a.getBoolean(R.styleable.Theme_isLightTheme, false);
// TODO: b/372558459 - Also check the background ColorDrawable color lightness
@@ -2108,6 +2105,14 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ private boolean shouldApplyForceInvertDark() {
+ final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
+ if (uiModeManager == null) {
+ return false;
+ }
+ return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
+ }
+
private void updateForceDarkMode() {
if (mAttachInfo.mThreadedRenderer == null) return;
if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
@@ -3474,6 +3479,9 @@ public final class ViewRootImpl implements ViewParent,
* TODO(b/260382739): Apply this to all windows.
*/
private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) {
+ if (com.android.window.flags.Flags.reduceUnnecessaryMeasure()) {
+ return true;
+ }
return (lp.privateFlags & PRIVATE_FLAG_OPTIMIZE_MEASURE) != 0;
}
@@ -9410,13 +9418,6 @@ public final class ViewRootImpl implements ViewParent,
return mAudioManager;
}
- private UiModeManager getUiModeManager() {
- if (mUiModeManager == null) {
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
- }
- return mUiModeManager;
- }
-
private Vibrator getSystemVibrator() {
if (mVibrator == null) {
mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 83dc79beb75e..315f1ba58529 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1514,6 +1514,44 @@ public interface WindowManager extends ViewManager {
"android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY";
/**
+ * Application or Activity level
+ * {@link android.content.pm.PackageManager.Property PackageManager.Property} that specifies
+ * whether this package or activity wants to allow safe region letterboxing. A safe
+ * region policy may be applied by the system to improve the user experience by ensuring that
+ * the activity does not have any content that is occluded and has the correct current
+ * window metrics.
+ *
+ * <p>Not setting the property at all defaults it to {@code true}. In such a case, the activity
+ * will be letterboxed in the safe region.
+ *
+ * <p>To not allow the safe region letterboxing, add this property to your app
+ * manifest and set the value to {@code false}. An app should ignore safe region
+ * letterboxing if it can handle bounds and insets from all four directions correctly when a
+ * request to go immersive is denied by the system. If the application does not allow safe
+ * region letterboxing, the system will not override this behavior.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;application&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING"
+ * android:value="false"/&gt;
+ * &lt;/application&gt;
+ * </pre>or
+ * <pre>
+ * &lt;activity&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING"
+ * android:value="false"/&gt;
+ * &lt;/activity&gt;
+ * </pre>
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ String PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING =
+ "android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING";
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e43fb48527a1..c97c4acf706c 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -866,31 +866,18 @@ public final class AccessibilityManager {
}
/**
- * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
- * for a given feedback type.
- *
- * @param feedbackTypeFlags The feedback type flags.
- * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
- *
- * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
- * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
- * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
- * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
- * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
- * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
+ * @see #getEnabledAccessibilityServiceList(int)
+ * @hide
*/
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
- int feedbackTypeFlags) {
+ int feedbackTypeFlags, int userId) {
final IAccessibilityManager service;
- final int userId;
synchronized (mLock) {
service = getServiceLocked();
if (service == null) {
return Collections.emptyList();
}
- userId = mUserId;
}
-
List<AccessibilityServiceInfo> services = null;
try {
services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
@@ -912,6 +899,29 @@ public final class AccessibilityManager {
}
/**
+ * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
+ * for a given feedback type.
+ *
+ * @param feedbackTypeFlags The feedback type flags.
+ * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+ *
+ * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+ * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+ * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+ * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+ * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
+ */
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+ int feedbackTypeFlags) {
+ final int userId;
+ synchronized (mLock) {
+ userId = mUserId;
+ }
+ return getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+ }
+
+ /**
* Returns whether the user must be shown the AccessibilityService warning dialog
* before the AccessibilityService (or any shortcut for the service) can be enabled.
* @hide
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 8c98fa455cc8..3780db38ec84 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -21,6 +21,7 @@ flag {
namespace: "pixel_state_server"
description: "Feature flag to send a flush event after each frame"
bug: "380381249"
+ is_exported: true
is_fixed_read_only: true
metadata {
purpose: PURPOSE_BUGFIX
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a41ab368aed8..b3bd89c2a87d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2454,6 +2454,7 @@ public final class InputMethodManager {
& WindowInsets.Type.ime()) == 0
|| viewRootImpl.getInsetsController()
.isPredictiveBackImeHideAnimInProgress())) {
+ Handler vh = view.getHandler();
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
if (resultReceiver != null) {
@@ -2464,8 +2465,17 @@ public final class InputMethodManager {
: InputMethodManager.RESULT_SHOWN, null);
}
// TODO(b/322992891) handle case of SHOW_IMPLICIT
- viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
- false /* fromIme */, statsToken);
+ if (vh.getLooper() != Looper.myLooper()) {
+ // The view is running on a different thread than our own, so
+ // we need to reschedule our work for over there.
+ if (DEBUG) Log.v(TAG, "Show soft input: reschedule to view thread");
+ final var finalStatsToken = statsToken;
+ vh.post(() -> viewRootImpl.getInsetsController().show(
+ WindowInsets.Type.ime(), false /* fromIme */, finalStatsToken));
+ } else {
+ viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
+ }
return true;
}
ImeTracker.forLogging().onCancelled(statsToken,
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3557f16a6dc8..ced27d6d4886 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UiThread;
import android.app.UriGrantsManager;
import android.content.ContentProvider;
import android.content.Intent;
@@ -38,6 +37,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignalBeamer;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -468,13 +468,27 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
});
}
+ /**
+ * Returns {@code false} if there is a sessionId mismatch and logs the event.
+ */
+ private boolean checkSessionId(@NonNull InputConnectionCommandHeader header) {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ Log.w(TAG, "Session id mismatch header.sessionId: " + header.mSessionId
+ + " currentSessionId: " + mCurrentSessionId.get() + " while calling "
+ + Debug.getCaller());
+ //TODO(b/396066692): log metrics.
+ return false; // cancelled
+ }
+ return true;
+ }
+
@Dispatching(cancellable = true)
@Override
public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextAfterCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -495,8 +509,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextBeforeCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -517,8 +531,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSelectedText(InputConnectionCommandHeader header, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getSelectedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -539,8 +553,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -567,8 +581,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsMode", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -584,8 +598,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
int flags, AndroidFuture future /* T=ExtractedText */) {
dispatchWithTracing("getExtractedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -601,8 +615,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("commitText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return;
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -618,8 +632,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -634,8 +648,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
dispatchWithTracing("commitCompletion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -650,8 +664,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
dispatchWithTracing("commitCorrection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -670,8 +684,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -686,8 +700,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -702,8 +716,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -718,8 +732,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setComposingRegion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -739,8 +753,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
int end, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -756,8 +770,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("setComposingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -773,8 +787,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -826,8 +840,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
}
return;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null && mDeactivateRequested.get()) {
@@ -842,8 +856,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEvent", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -858,8 +872,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStates", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -875,8 +889,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -892,8 +906,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
int beforeLength, int afterLength) {
dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -912,8 +926,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void beginBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("beginBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -928,8 +942,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void endBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("endBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -944,8 +958,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performSpellCheck(InputConnectionCommandHeader header) {
dispatchWithTracing("performSpellCheck", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -961,8 +975,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void performPrivateCommand(InputConnectionCommandHeader header, String action,
Bundle data) {
dispatchWithTracing("performPrivateCommand", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -995,12 +1009,12 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
}
}
dispatchWithTracing("performHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
if (resultReceiver != null) {
resultReceiver.send(
InputConnection.HANDWRITING_GESTURE_RESULT_CANCELLED, null);
}
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1038,9 +1052,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
(PreviewableHandwritingGesture) gestureContainer.get();
dispatchWithTracing("previewHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()
+ if (!checkSessionId(header)
|| (cancellationSignal != null && cancellationSignal.isCanceled())) {
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1065,8 +1079,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, 0 /* cursorUpdateFilter */, imeDisplayId);
@@ -1079,8 +1093,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
@@ -1123,9 +1137,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
InputConnectionCommandHeader header, RectF bounds,
@NonNull ResultReceiver resultReceiver) {
dispatchWithTracing("requestTextBoundsInfo", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1168,8 +1182,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
return false;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1193,8 +1207,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
dispatchWithTracing("setImeConsumesInput", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1217,8 +1231,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
dispatchWithTracing(
"replaceText",
() -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1236,8 +1250,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1256,8 +1270,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelectionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1273,8 +1287,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1301,8 +1315,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1317,8 +1331,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1333,8 +1347,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1349,8 +1363,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1366,8 +1380,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1382,8 +1396,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index a4ea64e5811e..cdca4102dd96 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -214,3 +214,24 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "invalidate_input_calls_restart"
+ namespace: "input_method"
+ description: "Feature flag to fix the race between invalidateInput and restartInput"
+ bug: "396066692"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "disallow_disabling_ime_navigation_bar"
+ namespace: "input_method"
+ description: "Disallows disabling the IME navigation bar through canImeRenderGesturalNavButtons"
+ bug: "402442590"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index cc2afbc6aaa3..d53c787749d9 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,7 +18,6 @@ package android.window;
import android.annotation.FloatRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.RemoteAnimationTarget;
@@ -39,8 +38,6 @@ public final class BackMotionEvent implements Parcelable {
@BackEvent.SwipeEdge
private final int mSwipeEdge;
- @Nullable
- private final RemoteAnimationTarget mDepartingAnimationTarget;
/**
* Creates a new {@link BackMotionEvent} instance.
@@ -53,8 +50,6 @@ public final class BackMotionEvent implements Parcelable {
* @param progress Value between 0 and 1 on how far along the back gesture is.
* @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
- * @param departingAnimationTarget The remote animation target of the departing
- * application window.
*/
public BackMotionEvent(
float touchX,
@@ -62,15 +57,13 @@ public final class BackMotionEvent implements Parcelable {
long frameTimeMillis,
float progress,
boolean triggerBack,
- @BackEvent.SwipeEdge int swipeEdge,
- @Nullable RemoteAnimationTarget departingAnimationTarget) {
+ @BackEvent.SwipeEdge int swipeEdge) {
mTouchX = touchX;
mTouchY = touchY;
mFrameTimeMillis = frameTimeMillis;
mProgress = progress;
mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
- mDepartingAnimationTarget = departingAnimationTarget;
}
private BackMotionEvent(@NonNull Parcel in) {
@@ -79,7 +72,6 @@ public final class BackMotionEvent implements Parcelable {
mProgress = in.readFloat();
mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
- mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
mFrameTimeMillis = in.readLong();
}
@@ -108,7 +100,6 @@ public final class BackMotionEvent implements Parcelable {
dest.writeFloat(mProgress);
dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
- dest.writeTypedObject(mDepartingAnimationTarget, flags);
dest.writeLong(mFrameTimeMillis);
}
@@ -160,16 +151,6 @@ public final class BackMotionEvent implements Parcelable {
return mFrameTimeMillis;
}
- /**
- * Returns the {@link RemoteAnimationTarget} of the top departing application window,
- * or {@code null} if the top window should not be moved for the current type of back
- * destination.
- */
- @Nullable
- public RemoteAnimationTarget getDepartingAnimationTarget() {
- return mDepartingAnimationTarget;
- }
-
@Override
public String toString() {
return "BackMotionEvent{"
@@ -179,7 +160,6 @@ public final class BackMotionEvent implements Parcelable {
+ ", mProgress=" + mProgress
+ ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge=" + mSwipeEdge
- + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
+ "}";
}
}
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 4908068d51e4..ea1b64066cfe 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,7 +20,6 @@ import android.annotation.FloatRange;
import android.os.SystemProperties;
import android.util.MathUtils;
import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
@@ -147,15 +146,14 @@ public class BackTouchTracker {
}
/** Creates a start {@link BackMotionEvent}. */
- public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+ public BackMotionEvent createStartEvent() {
return new BackMotionEvent(
/* touchX = */ mInitTouchX,
/* touchY = */ mInitTouchY,
/* frameTimeMillis = */ 0,
/* progress = */ 0,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ target);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Creates a progress {@link BackMotionEvent}. */
@@ -239,8 +237,7 @@ public class BackTouchTracker {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 866c16cb566d..5e8ce5ee557f 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -56,9 +56,13 @@ public enum DesktopExperienceFlags {
ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
true),
+ ENABLE_DISPLAY_DISCONNECT_INTERACTION(Flags::enableDisplayDisconnectInteraction, false),
ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
+ ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
+ ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
+ ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index aecf6eb261b1..5b3044e1988a 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -146,6 +146,8 @@ public enum DesktopModeFlags {
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES(
Flags::inheritTaskBoundsForTrampolineTaskLaunches, false),
+ SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX(
+ Flags::skipDecorViewRelayoutWhenClosingBugfix, false),
// go/keep-sorted end
;
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 6f4dd4e3c5ed..0b84070c8d26 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -66,17 +66,6 @@ interface IWindowOrganizerController {
void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t);
/**
- * Starts a legacy transition.
- * @param type The transition type.
- * @param adapter The animation to use.
- * @param syncCallback A sync callback for the contents of `t`
- * @param t Operations that are part of the transition.
- * @return sync-id or -1 if this no-op'd because a transition is already running.
- */
- int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
- in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
-
- /**
* Finishes a transition. This must be called for all created transitions.
* @param transitionToken Which transition to finish
* @param t Changes to make before finishing but in the same SF Transaction. Can be null.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108d928a..69613a748884 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,8 +270,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackStarted(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
@@ -286,8 +285,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackProgressed(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 485e7b33f3a7..2ed9c3a48add 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -445,6 +445,27 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ /**
+ * Sets a given safe region {@code Rect} on the {@code container}. Set {@code null} to reset
+ * safe region bounds. When a safe region is set on a WindowContainer, the activities which
+ * need to be within a safe region will be letterboxed within the set safe region bounds.
+ * <p>Note that if the position of the WindowContainer changes, the caller needs to update the
+ * safe region bounds.
+ *
+ * @param container The window container that the safe region bounds are set on
+ * @param safeRegionBounds The rect for the safe region bounds which are absolute in nature.
+ * @hide
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public WindowContainerTransaction setSafeRegionBounds(
+ @NonNull WindowContainerToken container,
+ @Nullable Rect safeRegionBounds) {
+ mHierarchyOps.add(
+ HierarchyOp.createForSetSafeRegionBounds(container.asBinder(), safeRegionBounds));
+ return this;
+ }
+
/*
* ===========================================================================================
* Hierarchy updates (create/destroy/reorder/reparent containers)
@@ -1597,6 +1618,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
public static final int HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK = 24;
public static final int HIERARCHY_OP_TYPE_APP_COMPAT_REACHABILITY = 25;
+ public static final int HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS = 26;
@IntDef(prefix = {"HIERARCHY_OP_TYPE_"}, value = {
HIERARCHY_OP_TYPE_REPARENT,
@@ -1625,6 +1647,7 @@ public final class WindowContainerTransaction implements Parcelable {
HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT,
HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK,
HIERARCHY_OP_TYPE_APP_COMPAT_REACHABILITY,
+ HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HierarchyOpType {
@@ -1710,6 +1733,9 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mLaunchAdjacentDisabled;
+ @Nullable
+ private Rect mSafeRegionBounds;
+
/** Creates a hierarchy operation for reparenting a container within the hierarchy. */
@NonNull
public static HierarchyOp createForReparent(
@@ -1873,6 +1899,17 @@ public final class WindowContainerTransaction implements Parcelable {
.build();
}
+ /** Creates a hierarchy op for setting the safe region bounds. */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public static HierarchyOp createForSetSafeRegionBounds(@NonNull IBinder container,
+ @Nullable Rect safeRegionBounds) {
+ return new Builder(HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS)
+ .setContainer(container)
+ .setSafeRegionBounds(safeRegionBounds)
+ .build();
+ }
+
/** Only creates through {@link Builder}. */
private HierarchyOp(@HierarchyOpType int type) {
mType = type;
@@ -1903,6 +1940,7 @@ public final class WindowContainerTransaction implements Parcelable {
mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents;
mExcludeInsetsTypes = copy.mExcludeInsetsTypes;
mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
+ mSafeRegionBounds = copy.mSafeRegionBounds;
}
private HierarchyOp(@NonNull Parcel in) {
@@ -1930,6 +1968,7 @@ public final class WindowContainerTransaction implements Parcelable {
mIsTrimmableFromRecents = in.readBoolean();
mExcludeInsetsTypes = in.readInt();
mLaunchAdjacentDisabled = in.readBoolean();
+ mSafeRegionBounds = in.readTypedObject(Rect.CREATOR);
}
@HierarchyOpType
@@ -2051,6 +2090,12 @@ public final class WindowContainerTransaction implements Parcelable {
return mLaunchAdjacentDisabled;
}
+ /** Denotes the safe region bounds */
+ @Nullable
+ public Rect getSafeRegionBounds() {
+ return mSafeRegionBounds;
+ }
+
/** Gets a string representation of a hierarchy-op type. */
public static String hopToString(@HierarchyOpType int type) {
switch (type) {
@@ -2084,6 +2129,7 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: return "restoreBackNav";
case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: return "setExcludeInsetsTypes";
case HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE: return "setKeyguardState";
+ case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS: return "setSafeRegionBounds";
default: return "HOP(" + type + ")";
}
}
@@ -2184,6 +2230,11 @@ public final class WindowContainerTransaction implements Parcelable {
sb.append("container= ").append(mContainer)
.append(" isTrimmable= ")
.append(mIsTrimmableFromRecents);
+ break;
+ case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS:
+ sb.append("container= ").append(mContainer)
+ .append(" safeRegionBounds= ")
+ .append(mSafeRegionBounds);
default:
sb.append("container=").append(mContainer)
.append(" reparent=").append(mReparent)
@@ -2220,6 +2271,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mIsTrimmableFromRecents);
dest.writeInt(mExcludeInsetsTypes);
dest.writeBoolean(mLaunchAdjacentDisabled);
+ dest.writeTypedObject(mSafeRegionBounds, flags);
}
@Override
@@ -2305,6 +2357,9 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mLaunchAdjacentDisabled;
+ @Nullable
+ private Rect mSafeRegionBounds;
+
Builder(@HierarchyOpType int type) {
mType = type;
}
@@ -2426,6 +2481,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setSafeRegionBounds(Rect safeRegionBounds) {
+ mSafeRegionBounds = safeRegionBounds;
+ return this;
+ }
+
@NonNull
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
@@ -2456,7 +2516,7 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents;
hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes;
hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled;
-
+ hierarchyOp.mSafeRegionBounds = mSafeRegionBounds;
return hierarchyOp;
}
}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 5c5da49fe543..6e56b6318727 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -130,27 +130,6 @@ public class WindowOrganizer {
}
/**
- * Start a legacy transition.
- * @param type The type of the transition. This is ignored if a transitionToken is provided.
- * @param adapter An existing transition to start. If null, a new transition is created.
- * @param t The set of window operations that are part of this transition.
- * @return true on success, false if a transition was already running.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- @NonNull
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull WindowContainerTransactionCallback syncCallback,
- @NonNull WindowContainerTransaction t) {
- try {
- return getWindowOrganizerController().startLegacyTransition(
- type, adapter, syncCallback.mInterface, t);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Register an ITransitionPlayer to handle transition animations.
* @hide
*/
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index a42759e9e23f..cc07616412b6 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -155,4 +155,12 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "backup_and_restore_for_user_aspect_ratio_settings"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether B&R for user aspect ratio settings is enabled"
+ bug: "396650383"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e706af999117..afc9660c1a3a 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -602,6 +602,13 @@ flag {
}
flag {
+ name: "keyboard_shortcuts_to_switch_desks"
+ namespace: "lse_desktop_experience"
+ description: "Enable switching the active desk with keyboard shortcuts"
+ bug: "389957556"
+}
+
+flag {
name: "enable_connected_displays_dnd"
namespace: "lse_desktop_experience"
description: "Enable drag-and-drop between connected displays."
@@ -926,6 +933,16 @@ flag {
}
flag {
+ name: "skip_decor_view_relayout_when_closing_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to skip DecorView relayout when the corresponding window is closing."
+ bug: "394502142"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_size_compat_mode_improvements_for_connected_displays"
namespace: "lse_desktop_experience"
description: "Enable some improvements in size compat mode for connected displays."
@@ -945,3 +962,20 @@ flag {
description: "Enable restart menu UI, which is shown when an app moves between displays."
bug: "397804287"
}
+
+flag {
+ name: "enable_dynamic_radius_computation_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
+ bug: "399630464"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "show_home_behind_desktop"
+ namespace: "lse_desktop_experience"
+ description: "Enables the home to be shown behind the desktop."
+ bug: "375644149"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 3927c713e500..816270235446 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -9,16 +9,6 @@ flag {
}
flag {
- name: "wait_for_transition_on_display_switch"
- namespace: "windowing_frontend"
- description: "Waits for Shell transition to start before unblocking the screen after display switch"
- bug: "301420598"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "apply_lifecycle_on_pip_change"
namespace: "windowing_frontend"
description: "Make pip activity lifecyle change with windowing mode"
@@ -29,17 +19,6 @@ flag {
}
flag {
- name: "respect_animation_clip"
- namespace: "windowing_frontend"
- description: "Fix missing clip transformation of animation"
- bug: "376601866"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "cache_window_style"
namespace: "windowing_frontend"
description: "Cache common window styles"
@@ -297,6 +276,17 @@ flag {
}
flag {
+ name: "reduce_unnecessary_measure"
+ namespace: "windowing_frontend"
+ description: "Skip measuring view hierarchy if the size is known"
+ bug: "260382739"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "ensure_wallpaper_in_transitions"
namespace: "windowing_frontend"
description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 1281a78d4fa2..24e80209e9e9 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -136,7 +136,8 @@ public class AccessibilityShortcutController {
private final Context mContext;
private final Handler mHandler;
- private final UserSetupCompleteObserver mUserSetupCompleteObserver;
+ @VisibleForTesting
+ public final UserSetupCompleteObserver mUserSetupCompleteObserver;
private AlertDialog mAlertDialog;
private boolean mIsShortcutEnabled;
@@ -471,7 +472,7 @@ public class AccessibilityShortcutController {
AccessibilityManager accessibilityManager =
mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
return accessibilityManager.getEnabledAccessibilityServiceList(
- FEEDBACK_ALL_MASK).contains(serviceInfo);
+ FEEDBACK_ALL_MASK, mUserId).contains(serviceInfo);
}
private boolean hasFeatureLeanback() {
@@ -676,7 +677,8 @@ public class AccessibilityShortcutController {
}
}
- private class UserSetupCompleteObserver extends ContentObserver {
+ @VisibleForTesting
+ public class UserSetupCompleteObserver extends ContentObserver {
private boolean mIsRegistered = false;
private int mUserId;
@@ -749,7 +751,8 @@ public class AccessibilityShortcutController {
R.string.config_defaultAccessibilityService);
final List<AccessibilityServiceInfo> enabledServices =
mFrameworkObjectProvider.getAccessibilityManagerInstance(
- mContext).getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
+ mContext).getEnabledAccessibilityServiceList(
+ FEEDBACK_ALL_MASK, mUserId);
for (int i = enabledServices.size() - 1; i >= 0; i--) {
if (TextUtils.equals(defaultShortcutTarget, enabledServices.get(i).getId())) {
return;
diff --git a/core/java/com/android/internal/app/MediaRouteChooserContentManager.java b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
new file mode 100644
index 000000000000..09c6f5e6caaa
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.android.internal.R;
+
+public class MediaRouteChooserContentManager {
+ Context mContext;
+
+ private final boolean mShowProgressBarWhenEmpty;
+
+ public MediaRouteChooserContentManager(Context context, boolean showProgressBarWhenEmpty) {
+ mContext = context;
+ mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ }
+
+ /**
+ * Starts binding all the views (list view, empty view, etc.) using the
+ * given container view.
+ */
+ public void bindViews(View containerView) {
+ View emptyView = containerView.findViewById(android.R.id.empty);
+ ListView listView = containerView.findViewById(R.id.media_route_list);
+ listView.setEmptyView(emptyView);
+
+ if (!mShowProgressBarWhenEmpty) {
+ containerView.findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+ // Center the empty view when the progress bar is not shown.
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+ params.gravity = Gravity.CENTER;
+ emptyView.setLayoutParams(params);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 23d966fdbc0d..5030a143ea94 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -23,14 +23,12 @@ import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -52,15 +50,15 @@ import java.util.Comparator;
public class MediaRouteChooserDialog extends AlertDialog {
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
- private final boolean mShowProgressBarWhenEmpty;
private int mRouteTypes;
private View.OnClickListener mExtendedSettingsClickListener;
private RouteAdapter mAdapter;
- private ListView mListView;
private Button mExtendedSettingsButton;
private boolean mAttachedToWindow;
+ private final MediaRouteChooserContentManager mContentManager;
+
public MediaRouteChooserDialog(Context context, int theme) {
this(context, theme, true);
}
@@ -70,7 +68,7 @@ public class MediaRouteChooserDialog extends AlertDialog {
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback();
- mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ mContentManager = new MediaRouteChooserContentManager(context, showProgressBarWhenEmpty);
}
/**
@@ -128,8 +126,9 @@ public class MediaRouteChooserDialog extends AlertDialog {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Note: setView must be called before super.onCreate().
- setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
- null));
+ View containerView = LayoutInflater.from(getContext()).inflate(
+ R.layout.media_route_chooser_dialog, null);
+ setView(containerView);
setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
? R.string.media_route_chooser_title_for_remote_display
@@ -140,25 +139,15 @@ public class MediaRouteChooserDialog extends AlertDialog {
super.onCreate(savedInstanceState);
- View emptyView = findViewById(android.R.id.empty);
mAdapter = new RouteAdapter(getContext());
- mListView = (ListView) findViewById(R.id.media_route_list);
- mListView.setAdapter(mAdapter);
- mListView.setOnItemClickListener(mAdapter);
- mListView.setEmptyView(emptyView);
+ ListView listView = findViewById(R.id.media_route_list);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener(mAdapter);
- mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
+ mExtendedSettingsButton = findViewById(R.id.media_route_extended_settings_button);
updateExtendedSettingsButton();
- if (!mShowProgressBarWhenEmpty) {
- findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
-
- // Center the empty view when the progress bar is not shown.
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) emptyView.getLayoutParams();
- params.gravity = Gravity.CENTER;
- emptyView.setLayoutParams(params);
- }
+ mContentManager.bindViews(containerView);
}
private void updateExtendedSettingsButton() {
@@ -240,8 +229,8 @@ public class MediaRouteChooserDialog extends AlertDialog {
view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
}
MediaRouter.RouteInfo route = getItem(position);
- TextView text1 = (TextView)view.findViewById(android.R.id.text1);
- TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+ TextView text1 = view.findViewById(android.R.id.text1);
+ TextView text2 = view.findViewById(android.R.id.text2);
text1.setText(route.getName());
CharSequence description = route.getDescription();
if (TextUtils.isEmpty(description)) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index c6207f9451c2..8151429f9139 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -672,6 +672,7 @@ public class BatteryStatsHistory {
*/
public void reset() {
synchronized (this) {
+ mMonotonicHistorySize = 0;
initHistoryBuffer();
if (mStore != null) {
mStore.reset();
diff --git a/core/java/com/android/internal/policy/DesktopModeCompatUtils.java b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java
new file mode 100644
index 000000000000..d7cfbdfed99c
--- /dev/null
+++ b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.window.DesktopModeFlags;
+
+/**
+ * Utility functions for app compat in desktop windowing used by both window manager and System UI.
+ * @hide
+ */
+public final class DesktopModeCompatUtils {
+
+ /**
+ * Whether the caption insets should be excluded from configuration for system to handle.
+ * The caller should ensure the activity is in or entering desktop view.
+ *
+ * <p> The treatment is enabled when all the of the following is true:
+ * <li> Any flags to forcibly consume caption insets are enabled.
+ * <li> Top activity have configuration coupled with insets.
+ * <li> Task is not resizeable or per-app override
+ * {@link ActivityInfo#OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS} is enabled.
+ */
+ public static boolean shouldExcludeCaptionFromAppBounds(@NonNull ActivityInfo info,
+ boolean isResizeable, boolean optOutEdgeToEdge) {
+ return DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue()
+ && isAnyForceConsumptionFlagsEnabled()
+ && !isConfigurationDecoupled(info, optOutEdgeToEdge)
+ && (!isResizeable
+ || info.isChangeEnabled(OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS));
+ }
+
+ private static boolean isConfigurationDecoupled(@NonNull ActivityInfo info,
+ boolean optOutEdgeToEdge) {
+ return info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED) && !optOutEdgeToEdge;
+ }
+
+ private static boolean isAnyForceConsumptionFlagsEnabled() {
+ return DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()
+ || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
index a53d6b899898..3fe6873028cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -132,8 +132,13 @@ public class AndroidPlatformSemanticNodeApplier
}
}
- // TODO correct values
- nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, -1, false));
+ nodeInfo.setClassName("android.widget.HorizontalScrollView");
+ } else {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ nodeInfo.setClassName("android.widget.ScrollView");
+ }
if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
nodeInfo.setClassName("android.widget.HorizontalScrollView");
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index d3a496db2ca9..db2c46046561 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.semantics.AccessibilitySem
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
+import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollDirection;
import java.util.ArrayList;
import java.util.Collections;
@@ -104,9 +105,9 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
if (isClickAction(action)) {
return performClick(component);
} else if (isScrollForwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, -500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.FORWARD);
} else if (isScrollBackwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, 500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.BACKWARD);
} else if (isShowOnScreenAction(action)) {
return showOnScreen(mRemoteContext, component);
} else {
@@ -141,17 +142,30 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
}
private boolean showOnScreen(RemoteContext context, Component component) {
- if (component.getParent() instanceof LayoutComponent) {
- LayoutComponent parent = (LayoutComponent) component.getParent();
+ ScrollableComponent scrollable = findScrollable(component);
+
+ if (scrollable != null) {
+ return scrollable.showOnScreen(context, component);
+ }
+
+ return false;
+ }
+
+ @Nullable
+ private static ScrollableComponent findScrollable(Component component) {
+ Component parent = component.getParent();
+
+ while (parent != null) {
ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
if (scrollable != null) {
- scrollable.showOnScreen(context, component.getComponentId());
- return true;
+ return scrollable;
+ } else {
+ parent = parent.getParent();
}
}
- return false;
+ return null;
}
/**
@@ -173,13 +187,32 @@ public class CoreDocumentAccessibility implements RemoteComposeDocumentAccessibi
}
/**
+ * scroll content in a given direction
+ *
+ * @param context
+ * @param component
+ * @param direction
+ * @return
+ */
+ public boolean scrollDirection(
+ RemoteContext context, Component component, ScrollDirection direction) {
+ ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);
+
+ if (scrollable != null) {
+ return scrollable.scrollDirection(context, direction);
+ }
+
+ return false;
+ }
+
+ /**
* Perform a click on the given component
*
* @param component
* @return
*/
public boolean performClick(Component component) {
- mDocument.performClick(mRemoteContext, component.getComponentId());
+ mDocument.performClick(mRemoteContext, component.getComponentId(), "");
return true;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index c38a44ac30be..da4e8d621602 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -39,6 +39,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
private final SemanticNodeApplier<AccessibilityNodeInfo> mApplier;
+ private final View mHost;
public PlatformRemoteComposeTouchHelper(
View host,
@@ -47,6 +48,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
super(host);
this.mRemoteDocA11y = remoteDocA11y;
this.mApplier = applier;
+ this.mHost = host;
}
public static PlatformRemoteComposeTouchHelper forRemoteComposePlayer(
@@ -150,6 +152,7 @@ public class PlatformRemoteComposeTouchHelper extends ExploreByTouchHelper {
boolean performed = mRemoteDocA11y.performAction(component, action, arguments);
if (performed) {
+ mHost.invalidate();
invalidateRoot();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 3cc998576741..e5c20eb7ce18 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -19,8 +19,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.DrawContent;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
@@ -29,6 +31,7 @@ import com.android.internal.widget.remotecompose.core.operations.RootContentBeha
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasOperations;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.Container;
@@ -42,6 +45,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.IntMa
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
import java.util.ArrayList;
import java.util.HashMap;
@@ -68,7 +73,9 @@ public class CoreDocument implements Serializable {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.4f;
+ static final float BUILD = 0.6f;
+
+ private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
@@ -442,6 +449,94 @@ public class CoreDocument implements Serializable {
return mDocProperties.get(key);
}
+ /**
+ * Apply a collection of operations to the document
+ *
+ * @param delta the delta to apply
+ */
+ public void applyUpdate(CoreDocument delta) {
+ HashMap<Integer, TextData> txtData = new HashMap<Integer, TextData>();
+ HashMap<Integer, BitmapData> imgData = new HashMap<Integer, BitmapData>();
+ HashMap<Integer, FloatConstant> fltData = new HashMap<Integer, FloatConstant>();
+ HashMap<Integer, IntegerConstant> intData = new HashMap<Integer, IntegerConstant>();
+ HashMap<Integer, LongConstant> longData = new HashMap<Integer, LongConstant>();
+ recursiveTreverse(
+ mOperations,
+ (op) -> {
+ if (op instanceof TextData) {
+ TextData d = (TextData) op;
+ txtData.put(d.mTextId, d);
+ } else if (op instanceof BitmapData) {
+ BitmapData d = (BitmapData) op;
+ imgData.put(d.mImageId, d);
+ } else if (op instanceof FloatConstant) {
+ FloatConstant d = (FloatConstant) op;
+ fltData.put(d.mId, d);
+ } else if (op instanceof IntegerConstant) {
+ IntegerConstant d = (IntegerConstant) op;
+ intData.put(d.mId, d);
+ } else if (op instanceof LongConstant) {
+ LongConstant d = (LongConstant) op;
+ longData.put(d.mId, d);
+ }
+ });
+
+ recursiveTreverse(
+ delta.mOperations,
+ (op) -> {
+ if (op instanceof TextData) {
+ TextData t = (TextData) op;
+ TextData txtInDoc = txtData.get(t.mTextId);
+ if (txtInDoc != null) {
+ txtInDoc.update(t);
+ Utils.log("update" + t.mText);
+ txtInDoc.markDirty();
+ }
+ } else if (op instanceof BitmapData) {
+ BitmapData b = (BitmapData) op;
+ BitmapData imgInDoc = imgData.get(b.mImageId);
+ if (imgInDoc != null) {
+ imgInDoc.update(b);
+ imgInDoc.markDirty();
+ }
+ } else if (op instanceof FloatConstant) {
+ FloatConstant f = (FloatConstant) op;
+ FloatConstant fltInDoc = fltData.get(f.mId);
+ if (fltInDoc != null) {
+ fltInDoc.update(f);
+ fltInDoc.markDirty();
+ }
+ } else if (op instanceof IntegerConstant) {
+ IntegerConstant ic = (IntegerConstant) op;
+ IntegerConstant intInDoc = intData.get(ic.mId);
+ if (intInDoc != null) {
+ intInDoc.update(ic);
+ intInDoc.markDirty();
+ }
+ } else if (op instanceof LongConstant) {
+ LongConstant lc = (LongConstant) op;
+ LongConstant longInDoc = longData.get(lc.mId);
+ if (longInDoc != null) {
+ longInDoc.update(lc);
+ longInDoc.markDirty();
+ }
+ }
+ });
+ }
+
+ private interface Visitor {
+ void visit(Operation op);
+ }
+
+ private void recursiveTreverse(ArrayList<Operation> mOperations, Visitor visitor) {
+ for (Operation op : mOperations) {
+ if (op instanceof Container) {
+ recursiveTreverse(((Component) op).mList, visitor);
+ }
+ visitor.visit(op);
+ }
+ }
+
// ============== Haptic support ==================
public interface HapticEngine {
/**
@@ -799,7 +894,10 @@ public class CoreDocument implements Serializable {
registerVariables(context, mOperations);
context.mMode = RemoteContext.ContextMode.UNSET;
- mFirstPaint = true;
+
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ mFirstPaint = true;
+ }
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -911,7 +1009,7 @@ public class CoreDocument implements Serializable {
*
* @param id the click area id
*/
- public void performClick(@NonNull RemoteContext context, int id) {
+ public void performClick(@NonNull RemoteContext context, int id, @NonNull String metadata) {
for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.mId == id) {
warnClickListeners(clickArea);
@@ -920,7 +1018,7 @@ public class CoreDocument implements Serializable {
}
for (IdActionCallback listener : mIdActionListeners) {
- listener.onAction(id, "");
+ listener.onAction(id, metadata);
}
Component component = getComponent(id);
@@ -1148,11 +1246,13 @@ public class CoreDocument implements Serializable {
context.mRemoteComposeState = mRemoteComposeState;
context.mRemoteComposeState.setContext(context);
- // Update any dirty variables
- if (mFirstPaint) {
- mFirstPaint = false;
- } else {
- updateVariables(context, theme, mOperations);
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ // Update any dirty variables
+ if (mFirstPaint) {
+ mFirstPaint = false;
+ } else {
+ updateVariables(context, theme, mOperations);
+ }
}
// If we have a content sizing set, we are going to take the original document
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 20897ba77de4..add9d5bae552 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -104,17 +104,20 @@ import com.android.internal.widget.remotecompose.core.operations.layout.managers
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DrawContentOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionMetadataOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation;
@@ -247,6 +250,7 @@ public class Operations {
public static final int LAYOUT_CANVAS_CONTENT = 207;
public static final int LAYOUT_TEXT = 208;
public static final int LAYOUT_STATE = 217;
+ public static final int LAYOUT_IMAGE = 234;
public static final int COMPONENT_START = 2;
@@ -254,6 +258,7 @@ public class Operations {
public static final int MODIFIER_HEIGHT = 67;
public static final int MODIFIER_WIDTH_IN = 231;
public static final int MODIFIER_HEIGHT_IN = 232;
+ public static final int MODIFIER_COLLAPSIBLE_PRIORITY = 235;
public static final int MODIFIER_BACKGROUND = 55;
public static final int MODIFIER_BORDER = 107;
public static final int MODIFIER_PADDING = 58;
@@ -278,6 +283,7 @@ public class Operations {
public static final int MODIFIER_VISIBILITY = 211;
public static final int HOST_ACTION = 209;
+ public static final int HOST_METADATA_ACTION = 216;
public static final int HOST_NAMED_ACTION = 210;
public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
@@ -364,6 +370,7 @@ public class Operations {
map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
+ map.put(MODIFIER_COLLAPSIBLE_PRIORITY, CollapsiblePriorityModifierOperation::read);
map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
map.put(MODIFIER_BORDER, BorderModifierOperation::read);
@@ -385,6 +392,7 @@ public class Operations {
map.put(CONTAINER_END, ContainerEnd::read);
map.put(HOST_ACTION, HostActionOperation::read);
+ map.put(HOST_METADATA_ACTION, HostActionMetadataOperation::read);
map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
map.put(
@@ -407,7 +415,7 @@ public class Operations {
map.put(LAYOUT_CANVAS, CanvasLayout::read);
map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
map.put(LAYOUT_TEXT, TextLayout::read);
-
+ map.put(LAYOUT_IMAGE, ImageLayout::read);
map.put(LAYOUT_STATE, StateLayout::read);
map.put(DRAW_CONTENT, DrawContent::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 161a5f17ef23..1f026687680f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -102,6 +102,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.managers
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
@@ -2094,6 +2095,19 @@ public class RemoteComposeBuffer {
}
/**
+ * Add an imagelayout command
+ *
+ * @param componentId component id
+ * @param animationId animation id
+ * @param bitmapId bitmap id
+ */
+ public void addImage(
+ int componentId, int animationId, int bitmapId, int scaleType, float alpha) {
+ mLastComponentId = getComponentId(componentId);
+ ImageLayout.apply(mBuffer, componentId, animationId, bitmapId, scaleType, alpha);
+ }
+
+ /**
* Add a row start tag
*
* @param componentId component id
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index e37833f33fa5..b297a023d03b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -46,7 +46,7 @@ public abstract class RemoteContext {
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
-
+ private long mDocLoadTime = System.currentTimeMillis();
@Nullable protected PaintContext mPaintContext = null;
protected float mDensity = Float.NaN;
@@ -83,6 +83,20 @@ public abstract class RemoteContext {
}
}
+ /**
+ * Get the time the document was loaded
+ *
+ * @return time in ms since the document was loaded
+ */
+ public long getDocLoadTime() {
+ return mDocLoadTime;
+ }
+
+ /** Set the time the document was loaded */
+ public void setDocLoadTime() {
+ mDocLoadTime = System.currentTimeMillis();
+ }
+
public boolean isAnimationEnabled() {
return mAnimate;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 255d7a46e49e..90929e06ecd0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -41,12 +41,12 @@ import java.util.List;
public class BitmapData extends Operation implements SerializableToString, Serializable {
private static final int OP_CODE = Operations.DATA_BITMAP;
private static final String CLASS_NAME = "BitmapData";
- int mImageId;
+ public final int mImageId;
int mImageWidth;
int mImageHeight;
short mType;
short mEncoding;
- @NonNull final byte[] mBitmap;
+ @NonNull byte[] mBitmap;
/** The max size of width or height */
public static final int MAX_IMAGE_DIMENSION = 8000;
@@ -91,6 +91,19 @@ public class BitmapData extends Operation implements SerializableToString, Seria
}
/**
+ * Update the bitmap data
+ *
+ * @param from the bitmap to copy
+ */
+ public void update(BitmapData from) {
+ this.mImageWidth = from.mImageWidth;
+ this.mImageHeight = from.mImageHeight;
+ this.mBitmap = from.mBitmap;
+ this.mType = from.mType;
+ this.mEncoding = from.mEncoding;
+ }
+
+ /**
* The width of the image
*
* @return the width
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 233e246d3868..5096aaf03c9d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -42,6 +42,15 @@ public class FloatConstant extends Operation implements Serializable {
this.mValue = value;
}
+ /**
+ * Copy the value from another operation
+ *
+ * @param from value to copy from
+ */
+ public void update(FloatConstant from) {
+ mValue = from.mValue;
+ }
+
@Override
public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValue);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index 67773d1d6187..d8ef4cbba4d6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -37,7 +37,7 @@ public class TextData extends Operation implements SerializableToString, Seriali
private static final int OP_CODE = Operations.DATA_TEXT;
private static final String CLASS_NAME = "TextData";
public final int mTextId;
- @NonNull public final String mText;
+ @NonNull public String mText;
public static final int MAX_STRING_SIZE = 4000;
public TextData(int textId, @NonNull String text) {
@@ -45,6 +45,15 @@ public class TextData extends Operation implements SerializableToString, Seriali
this.mText = text;
}
+ /**
+ * Copy the data from another text data
+ *
+ * @param from source to copy from
+ */
+ public void update(TextData from) {
+ mText = from.mText;
+ }
+
@Override
public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mText);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index e9cc26f58c9b..dee79a45de3d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -85,6 +85,9 @@ public class TimeAttribute extends PaintOperation {
/** the year */
public static final short TIME_YEAR = 12;
+ /** (value - doc_load_time) * 1E-3 */
+ public static final short TIME_FROM_LOAD_SEC = 14;
+
/**
* creates a new operation
*
@@ -226,6 +229,7 @@ public class TimeAttribute extends PaintOperation {
int val = mType & 255;
int flags = mType >> 8;
RemoteContext ctx = context.getContext();
+ long load_time = ctx.getDocLoadTime();
LongConstant longConstant = (LongConstant) ctx.getObject(mTimeId);
long value = longConstant.getValue();
long delta = 0;
@@ -292,6 +296,9 @@ public class TimeAttribute extends PaintOperation {
case TIME_YEAR:
ctx.loadFloat(mId, time.getYear());
break;
+ case TIME_FROM_LOAD_SEC:
+ ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+ break;
}
}
@@ -334,6 +341,8 @@ public class TimeAttribute extends PaintOperation {
return "TIME_DAY_OF_WEEK";
case TIME_YEAR:
return "TIME_YEAR";
+ case TIME_FROM_LOAD_SEC:
+ return "TIME_FROM_LOAD_SEC";
default:
return "INVALID_TIME_TYPE";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index 76bb96d1b61a..2a809c6f0a2a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -27,6 +27,7 @@ import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
@@ -823,15 +824,27 @@ public class Component extends PaintOperation
*
* @param value a 2 dimension float array that will receive the horizontal and vertical position
* of the component.
+ * @param forSelf whether the location is for this container or a child, relevant for scrollable
+ * items.
*/
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX;
value[1] += mY;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
+ /**
+ * Returns the location of the component relative to the root component
+ *
+ * @param value a 2 dimension float array that will receive the horizontal and vertical position
+ * of the component.
+ */
+ public void getLocationInWindow(@NonNull float[] value) {
+ getLocationInWindow(value, true);
+ }
+
@NonNull
@Override
public String toString() {
@@ -1131,14 +1144,14 @@ public class Component extends PaintOperation
}
/**
- * Extract child TextData elements
+ * Extract child Data elements
*
- * @param data an ArrayList that will be populated with the TextData elements (if any)
+ * @param data an ArrayList that will be populated with the Data elements (if any)
*/
- public void getData(@NonNull ArrayList<TextData> data) {
+ public void getData(@NonNull ArrayList<Operation> data) {
for (Operation op : mList) {
- if (op instanceof TextData) {
- data.add((TextData) op);
+ if (op instanceof TextData || op instanceof BitmapData) {
+ data.add(op);
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index e57438662012..bc099e3a3b9d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -30,7 +30,6 @@ import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
-import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
@@ -138,7 +137,7 @@ public class LayoutComponent extends Component {
@Override
public void inflate() {
- ArrayList<TextData> data = new ArrayList<>();
+ ArrayList<Operation> data = new ArrayList<>();
ArrayList<Operation> supportedOperations = new ArrayList<>();
for (Operation op : mList) {
@@ -186,8 +185,6 @@ public class LayoutComponent extends Component {
((ScrollModifierOperation) op).inflate(this);
}
mComponentModifiers.add((ModifierOperation) op);
- } else if (op instanceof TextData) {
- data.add((TextData) op);
} else if (op instanceof TouchExpression
|| (op instanceof PaintData)
|| (op instanceof FloatExpression)) {
@@ -292,11 +289,11 @@ public class LayoutComponent extends Component {
}
@Override
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX + mPaddingLeft;
value[1] += mY + mPaddingTop;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
index b0089525af5a..00ec60533087 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -24,10 +24,13 @@ import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleColumnLayout extends ColumnLayout {
@@ -153,7 +156,7 @@ public class CollapsibleColumnLayout extends ColumnLayout {
}
@Override
- protected boolean hasVerticalIntrinsicDimension() {
+ public boolean hasVerticalIntrinsicDimension() {
return true;
}
@@ -166,25 +169,72 @@ public class CollapsibleColumnLayout extends ColumnLayout {
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
float currentMaxHeight = maxHeight;
+ boolean hasPriorities = false;
for (Component c : mChildrenComponents) {
- if (c instanceof CollapsibleColumnLayout) {
- c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
- } else {
- c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleColumnLayout) {
+ c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
+ } else {
+ c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ }
}
+
ComponentMeasure m = measure.get(c);
if (!m.isGone()) {
- size.setWidth(Math.max(size.getWidth(), m.getW()));
- size.setHeight(size.getHeight() + m.getH());
+ if (size != null) {
+ size.setWidth(Math.max(size.getWidth(), m.getW()));
+ size.setHeight(size.getHeight() + m.getH());
+ }
visibleChildren++;
currentMaxHeight -= m.getH();
}
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
}
- if (!mChildrenComponents.isEmpty()) {
+ if (!mChildrenComponents.isEmpty() && size != null) {
size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildren - 1)));
}
@@ -192,7 +242,14 @@ public class CollapsibleColumnLayout extends ColumnLayout {
float childrenHeight = 0f;
boolean overflow = false;
- for (Component child : mChildrenComponents) {
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.VERTICAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
if (overflow || childMeasure.isGone()) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
@@ -209,10 +266,10 @@ public class CollapsibleColumnLayout extends ColumnLayout {
visibleChildren++;
}
}
- if (verticalWrap) {
+ if (verticalWrap && size != null) {
size.setHeight(Math.min(maxHeight, childrenHeight));
}
- if (visibleChildren == 0 || size.getHeight() <= 0f) {
+ if (visibleChildren == 0 || (size != null && size.getHeight() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
new file mode 100644
index 000000000000..46cd45ecba8a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+
+import java.util.ArrayList;
+
+/** Utility class to manage collapsible priorities on components */
+public class CollapsiblePriority {
+
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
+ /**
+ * Returns the priority of a child component
+ *
+ * @param c the child component
+ * @return priority value, or 0f if not found
+ */
+ static float getPriority(Component c, int orientation) {
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null && priority.getOrientation() == orientation) {
+ return priority.getPriority();
+ }
+ }
+ return Float.MAX_VALUE;
+ }
+
+ /**
+ * Allocate and return a sorted array of components by their priorities
+ *
+ * @param components the children components
+ * @return list of components sorted by their priority in decreasing order
+ */
+ static ArrayList<Component> sortWithPriorities(
+ ArrayList<Component> components, int orientation) {
+ ArrayList<Component> sorted = new ArrayList<>(components);
+ sorted.sort(
+ (t1, t2) -> {
+ float p1 = getPriority(t1, orientation);
+ float p2 = getPriority(t2, orientation);
+ return (int) (p2 - p1);
+ });
+ return sorted;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
index 05f332960c16..e3632f9888ec 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -24,10 +24,13 @@ import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleRowLayout extends RowLayout {
@@ -135,8 +138,12 @@ public class CollapsibleRowLayout extends RowLayout {
}
@Override
- protected boolean hasHorizontalIntrinsicDimension() {
- return true;
+ public float minIntrinsicHeight(@NonNull RemoteContext context) {
+ float height = computeModifierDefinedHeight(context);
+ if (!mChildrenComponents.isEmpty()) {
+ height += mChildrenComponents.get(0).minIntrinsicHeight(context);
+ }
+ return height;
}
@Override
@@ -149,12 +156,8 @@ public class CollapsibleRowLayout extends RowLayout {
}
@Override
- public float minIntrinsicHeight(@NonNull RemoteContext context) {
- float height = computeModifierDefinedHeight(context);
- if (!mChildrenComponents.isEmpty()) {
- height += mChildrenComponents.get(0).minIntrinsicHeight(context);
- }
- return height;
+ public boolean hasHorizontalIntrinsicDimension() {
+ return true;
}
@Override
@@ -166,45 +169,107 @@ public class CollapsibleRowLayout extends RowLayout {
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
- super.computeWrapSize(
- context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
}
@Override
- public boolean applyVisibility(
- float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
- float childrenWidth = 0f;
- float childrenHeight = 0f;
- boolean changedVisibility = false;
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
- self.clearVisibilityOverride();
- if (selfWidth <= 0 || selfHeight <= 0) {
- self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- return true;
+ self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
+ float currentMaxWidth = maxWidth;
+ boolean hasPriorities = false;
+ for (Component c : mChildrenComponents) {
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleRowLayout) {
+ c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
+ } else {
+ c.measure(context, 0f, Float.MAX_VALUE, 0f, maxHeight, measure);
+ }
+ }
+ ComponentMeasure m = measure.get(c);
+ if (!m.isGone()) {
+ if (size != null) {
+ size.setHeight(Math.max(size.getHeight(), m.getH()));
+ size.setWidth(size.getWidth() + m.getW());
+ }
+ visibleChildren++;
+ currentMaxWidth -= m.getW();
+ }
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
+ }
+ if (!mChildrenComponents.isEmpty() && size != null) {
+ size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildren - 1)));
}
- for (Component child : mChildrenComponents) {
+
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+
+ boolean overflow = false;
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.HORIZONTAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
- int visibility = childMeasure.getVisibility();
- childMeasure.clearVisibilityOverride();
- if (!childMeasure.isVisible()) {
+ if (overflow || childMeasure.isGone()) {
+ childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
continue;
}
- if (childrenWidth + childMeasure.getW() > selfWidth
- && childrenHeight + childMeasure.getH() > selfHeight) {
+ float childWidth = childMeasure.getW();
+ boolean childDoesNotFits = childrenWidth + childWidth > maxWidth;
+ if (childDoesNotFits) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- if (visibility != childMeasure.getVisibility()) {
- changedVisibility = true;
- }
+ overflow = true;
} else {
- childrenWidth += childMeasure.getW();
+ childrenWidth += childWidth;
childrenHeight = Math.max(childrenHeight, childMeasure.getH());
visibleChildren++;
}
}
- if (visibleChildren == 0) {
+ if (horizontalWrap && size != null) {
+ size.setWidth(Math.min(maxWidth, childrenWidth));
+ }
+ if (visibleChildren == 0 || (size != null && size.getWidth() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
- return changedVisibility;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index cda90c2d3b0b..9566242ccbc5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -372,6 +373,17 @@ public class ColumnLayout extends LayoutManager {
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mVerticalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mVerticalScrollDelegate;
+
+ value[1] += smo.getScrollY();
+ }
+ }
+
/**
* The name of the class
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java
new file mode 100644
index 000000000000..a4ed0c1319d4
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
+import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+public class ImageLayout extends LayoutManager implements VariableSupport {
+
+ private static final boolean DEBUG = false;
+ private int mBitmapId = -1;
+ private int mScaleType;
+ private float mAlpha = 1f;
+
+ @NonNull ImageScaling mScaling = new ImageScaling();
+ @NonNull PaintBundle mPaint = new PaintBundle();
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ if (mBitmapId != -1) {
+ context.listensTo(mBitmapId, this);
+ }
+ }
+
+ public ImageLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int bitmapId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int scaleType,
+ float alpha) {
+ super(parent, componentId, animationId, x, y, width, height);
+ mBitmapId = bitmapId;
+ mScaleType = scaleType & 0xFF;
+ mAlpha = alpha;
+ }
+
+ public ImageLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int bitmapId,
+ int scaleType,
+ float alpha) {
+ this(parent, componentId, animationId, bitmapId, 0, 0, 0, 0, scaleType, alpha);
+ }
+
+ @Override
+ public void computeWrapSize(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
+
+ BitmapData bitmapData = (BitmapData) context.getContext().getObject(mBitmapId);
+ if (bitmapData != null) {
+ size.setWidth(bitmapData.getWidth());
+ size.setHeight(bitmapData.getHeight());
+ }
+ }
+
+ @Override
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ float modifiersWidth = computeModifierDefinedWidth(context.getContext());
+ float modifiersHeight = computeModifierDefinedHeight(context.getContext());
+ ComponentMeasure m = measure.get(this);
+ m.setW(modifiersWidth);
+ m.setH(modifiersHeight);
+ }
+
+ @Override
+ public void paintingComponent(@NonNull PaintContext context) {
+ context.save();
+ context.translate(mX, mY);
+ mComponentModifiers.paint(context);
+ float tx = mPaddingLeft;
+ float ty = mPaddingTop;
+ context.translate(tx, ty);
+ float w = mWidth - mPaddingLeft - mPaddingRight;
+ float h = mHeight - mPaddingTop - mPaddingBottom;
+ context.clipRect(0f, 0f, w, h);
+
+ BitmapData bitmapData = (BitmapData) context.getContext().getObject(mBitmapId);
+ if (bitmapData != null) {
+ mScaling.setup(
+ 0f,
+ 0f,
+ bitmapData.getWidth(),
+ bitmapData.getHeight(),
+ 0f,
+ 0f,
+ w,
+ h,
+ mScaleType,
+ 1f);
+
+ context.savePaint();
+ if (mAlpha == 1f) {
+ context.drawBitmap(
+ mBitmapId,
+ (int) 0f,
+ (int) 0f,
+ (int) bitmapData.getWidth(),
+ (int) bitmapData.getHeight(),
+ (int) mScaling.mFinalDstLeft,
+ (int) mScaling.mFinalDstTop,
+ (int) mScaling.mFinalDstRight,
+ (int) mScaling.mFinalDstBottom,
+ -1);
+ } else {
+ context.savePaint();
+ mPaint.reset();
+ mPaint.setColor(0f, 0f, 0f, mAlpha);
+ context.applyPaint(mPaint);
+ context.drawBitmap(
+ mBitmapId,
+ (int) 0f,
+ (int) 0f,
+ (int) bitmapData.getWidth(),
+ (int) bitmapData.getHeight(),
+ (int) mScaling.mFinalDstLeft,
+ (int) mScaling.mFinalDstTop,
+ (int) mScaling.mFinalDstRight,
+ (int) mScaling.mFinalDstBottom,
+ -1);
+ context.restorePaint();
+ }
+ context.restorePaint();
+ }
+
+ // debugBox(this, context);
+ context.translate(-tx, -ty);
+ context.restore();
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "IMAGE_LAYOUT ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
+ }
+
+ @NonNull
+ @Override
+ protected String getSerializedName() {
+ return "IMAGE_LAYOUT";
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(
+ indent,
+ getSerializedName()
+ + " ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = "
+ + "["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + mVisibility
+ + " ("
+ + mBitmapId
+ + "\")");
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return "ImageLayout";
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return Operations.LAYOUT_IMAGE;
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer
+ * @param componentId
+ * @param animationId
+ * @param bitmapId
+ * @param scaleType
+ * @param alpha
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int bitmapId,
+ int scaleType,
+ float alpha) {
+ buffer.start(id());
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(bitmapId);
+ buffer.writeInt(scaleType);
+ buffer.writeFloat(alpha);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ int bitmapId = buffer.readInt();
+ int scaleType = buffer.readInt();
+ float alpha = buffer.readFloat();
+ operations.add(new ImageLayout(null, componentId, animationId, bitmapId, scaleType, alpha));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", id(), name())
+ .description("Image layout implementation.\n\n")
+ .field(INT, "COMPONENT_ID", "unique id for this component")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
+ .field(INT, "BITMAP_ID", "bitmap id")
+ .field(INT, "SCALE_TYPE", "scale type")
+ .field(FLOAT, "ALPHA", "alpha");
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mComponentId, mAnimationId, mBitmapId, mScaleType, mAlpha);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 5b66b95cf1dd..eb10ead34781 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -226,9 +226,17 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
measure,
mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
- computeSize(context, 0f, w, 0, measuredHeight, measure);
if (hasHorizontalScroll()) {
+ computeSize(context, 0f, w, 0, measuredHeight, measure);
mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else if (hasVerticalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
@@ -236,9 +244,17 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
computeWrapSize(
context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
- computeSize(context, 0f, measuredWidth, 0, h, measure);
if (hasVerticalScroll()) {
+ computeSize(context, 0f, measuredWidth, 0, h, measure);
mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else {
float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index d5d2e03c3f2a..15b54a3ce994 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -386,6 +387,17 @@ public class RowLayout extends LayoutManager {
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mHorizontalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mHorizontalScrollDelegate;
+
+ value[0] += smo.getScrollX();
+ }
+ }
+
/**
* The name of the class
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index d383ee9e4fc9..120c740eccda 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -77,6 +77,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
private final Size mCachedSize = new Size(0f, 0f);
@Nullable private String mCachedString = "";
+ @Nullable private String mNewString;
Platform.ComputedTextLayout mComputedTextLayout;
@@ -99,7 +100,7 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
return;
}
- mCachedString = cachedString;
+ mNewString = cachedString;
if (mType == -1) {
if (mFontFamilyId != -1) {
String fontFamily = context.getText(mFontFamilyId);
@@ -119,8 +120,6 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
mType = 0;
}
}
- mTextW = -1;
- mTextH = -1;
if (mHorizontalScrollDelegate != null) {
mHorizontalScrollDelegate.reset();
@@ -351,6 +350,9 @@ public class TextLayout extends LayoutManager implements VariableSupport, Access
mPaint.setColor(mColor);
context.replacePaint(mPaint);
float[] bounds = new float[4];
+ if (mNewString != null && !mNewString.equals(mCachedString)) {
+ mCachedString = mNewString;
+ }
if (mCachedString == null) {
return;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 656a3c0fca68..b3d76b765143 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -250,7 +250,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
context.savePaint();
paint.reset();
paint.setColor(mR, mG, mB, mA);
- paint.setStrokeWidth(mBorderWidth);
+ paint.setStrokeWidth(mBorderWidth * context.getContext().getDensity());
paint.setStyle(PaintBundle.STYLE_STROKE);
context.replacePaint(paint);
if (mShapeType == ShapeType.RECTANGLE) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
new file mode 100644
index 000000000000..b1f2d2d35b93
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Set an optional priority on a component within a collapsible layout */
+public class CollapsiblePriorityModifierOperation extends Operation
+ implements ModifierOperation, Serializable {
+ private static final int OP_CODE = Operations.MODIFIER_COLLAPSIBLE_PRIORITY;
+ public static final String CLASS_NAME = "CollapsiblePriorityModifierOperation";
+
+ private float mPriority;
+ private int mOrientation;
+
+ public CollapsiblePriorityModifierOperation(int orientation, float priority) {
+ mOrientation = orientation;
+ mPriority = priority;
+ }
+
+ public float getPriority() {
+ return mPriority;
+ }
+
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mOrientation, mPriority);
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ // nothing
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return "";
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int orientation = buffer.readInt();
+ float priority = buffer.readFloat();
+ operations.add(new CollapsiblePriorityModifierOperation(orientation, priority));
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "CollapsiblePriorityModifier")
+ .description("Add additional priority to children of Collapsible layouts")
+ .field(DocumentedOperation.INT, "orientation", "Horizontal(0) or Vertical (1)")
+ .field(DocumentedOperation.FLOAT, "priority", "The associated priority");
+ }
+
+ /**
+ * Writes out the CollapsiblePriorityModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param priority priority value
+ * @param orientation orientation (HORIZONTAL or VERTICAL)
+ */
+ public static void apply(@NonNull WireBuffer buffer, int orientation, float priority) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(orientation);
+ buffer.writeFloat(priority);
+ }
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ serializer
+ .addTags(SerializeTags.MODIFIER)
+ .addType(name())
+ .add("orientation", mOrientation)
+ .add("priority", mPriority);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "PRIORITY = [" + getPriority() + "] (" + mOrientation + ")");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java
new file mode 100644
index 000000000000..2170efd68a64
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Capture a host action information. This can be triggered on eg. a click. */
+public class HostActionMetadataOperation extends Operation
+ implements ActionOperation, SerializableToString, Serializable {
+ private static final int OP_CODE = Operations.HOST_METADATA_ACTION;
+
+ int mActionId = -1;
+ int mMetadataId = -1;
+
+ public HostActionMetadataOperation(int id, int metadataId) {
+ mActionId = id;
+ mMetadataId = metadataId;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "HostMetadataActionOperation(" + mActionId + ":" + mMetadataId + ")";
+ }
+
+ public int getActionId() {
+ return mActionId;
+ }
+
+ /**
+ * Returns the serialized name for this operation
+ *
+ * @return the serialized name
+ */
+ @NonNull
+ public String serializedName() {
+ return "HOST_METADATA_ACTION";
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, serializedName() + " = " + mActionId + ", " + mMetadataId);
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {}
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {}
+
+ @Override
+ public void runAction(
+ @NonNull RemoteContext context,
+ @NonNull CoreDocument document,
+ @NonNull Component component,
+ float x,
+ float y) {
+ String metadata = context.getText(mMetadataId);
+ if (metadata == null) {
+ metadata = "";
+ }
+ context.runAction(mActionId, metadata);
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer a WireBuffer
+ * @param actionId the action id
+ */
+ public static void apply(@NonNull WireBuffer buffer, int actionId, int metadataId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(actionId);
+ buffer.writeInt(metadataId);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int actionId = buffer.readInt();
+ int metadataId = buffer.readInt();
+ operations.add(new HostActionMetadataOperation(actionId, metadataId));
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "HostAction")
+ .description("Host action. This operation represents a host action")
+ .field(INT, "ACTION_ID", "Host Action ID")
+ .field(INT, "METADATA", "Host Action Text Metadata ID");
+ }
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ serializer
+ .addTags(SerializeTags.MODIFIER)
+ .addType("HostActionOperation")
+ .add("id", mActionId)
+ .add("metadata", mMetadataId);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 3e1f32de66e4..42692f95fcda 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -430,9 +430,35 @@ public class ScrollModifierOperation extends ListActionsOperation
}
@Override
- public boolean showOnScreen(RemoteContext context, int childId) {
- // TODO correct this when we trust the bounds in parent
- return scrollByOffset(context, -1000) != 0;
+ public boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ float offset = mHostDimension * 0.7f;
+
+ if (direction == ScrollDirection.FORWARD
+ || direction == ScrollDirection.DOWN
+ || direction == ScrollDirection.RIGHT) {
+ offset *= -1;
+ }
+
+ return scrollByOffset(context, (int) offset) != 0;
+ }
+
+ @Override
+ public boolean showOnScreen(RemoteContext context, Component child) {
+ float[] locationInWindow = new float[2];
+ child.getLocationInWindow(locationInWindow);
+
+ int offset = 0;
+ if (handlesVerticalScroll()) {
+ offset = (int) -locationInWindow[1];
+ } else {
+ offset = (int) -locationInWindow[0];
+ }
+
+ if (offset == 0) {
+ return true;
+ } else {
+ return scrollByOffset(context, offset) != 0;
+ }
}
@Nullable
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index a95a175d0edd..120c7ac9efbf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -35,7 +35,10 @@ public class StringUtils {
@NonNull
public static String floatToString(
float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
-
+ boolean isNeg = value < 0;
+ if (isNeg) {
+ value = -value;
+ }
int integerPart = (int) value;
float fractionalPart = value % 1;
@@ -54,14 +57,13 @@ public class StringUtils {
integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
}
if (afterDecimalPoint == 0) {
- return integerPartString;
+ return ((isNeg) ? "-" : "") + integerPartString;
}
// Convert fractional part to string and pad with zeros
for (int i = 0; i < afterDecimalPoint; i++) {
fractionalPart *= 10;
}
-
fractionalPart = Math.round(fractionalPart);
for (int i = 0; i < afterDecimalPoint; i++) {
@@ -87,6 +89,6 @@ public class StringUtils {
fact = fact + new String(c);
}
- return integerPartString + "." + fact;
+ return ((isNeg) ? "-" : "") + integerPartString + "." + fact;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
index 3d1bd12357c9..1610e6332c1c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
@@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.core.semantics;
import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
/**
* Interface for components that support scrolling.
@@ -48,13 +49,23 @@ public interface ScrollableComponent extends AccessibilitySemantics {
}
/**
+ * Scrolls the content in the specified direction.
+ *
+ * @param direction the direction to scroll
+ * @return whether a scroll was possible
+ */
+ default boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ return false;
+ }
+
+ /**
* Show a child with the given ID on the screen, typically scrolling so it's fully on screen.
*
- * @param childId The ID of the child to check for visibility.
+ * @param child The child (including nested) to check for visibility.
* @return {@code true} if the child with the given ID could be shown on screen; {@code false}
* otherwise.
*/
- default boolean showOnScreen(RemoteContext context, int childId) {
+ default boolean showOnScreen(RemoteContext context, Component child) {
return false;
}
@@ -108,4 +119,13 @@ public interface ScrollableComponent extends AccessibilitySemantics {
return mCanScrollBackwards;
}
}
+
+ enum ScrollDirection {
+ FORWARD,
+ BACKWARD,
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index bdc765968387..25dcb67fe9e2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -34,14 +34,23 @@ import java.util.List;
public class IntegerConstant extends Operation implements Serializable {
private static final String CLASS_NAME = "IntegerConstant";
- private final int mValue;
- private final int mId;
+ private int mValue;
+ public final int mId;
IntegerConstant(int id, int value) {
mId = id;
mValue = value;
}
+ /**
+ * Updates the value of the integer constant
+ *
+ * @param ic the integer constant to copy
+ */
+ public void update(IntegerConstant ic) {
+ mValue = ic.mValue;
+ }
+
@Override
public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValue);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index d071e0a21d22..ab0f7352182a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -36,7 +36,7 @@ public class LongConstant extends Operation implements Serializable {
private static final int OP_CODE = Operations.DATA_LONG;
private long mValue;
- private final int mId;
+ public final int mId;
/**
* @param id the id of the constant
@@ -48,6 +48,15 @@ public class LongConstant extends Operation implements Serializable {
}
/**
+ * Copy the value from another longConstant
+ *
+ * @param from the constant to copy from
+ */
+ public void update(LongConstant from) {
+ mValue = from.mValue;
+ }
+
+ /**
* Get the value of the long constant
*
* @return the value of the long
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 1f9a27429067..f5b2cca15e43 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -41,6 +41,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.RemoteContextAware;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
+import com.android.internal.widget.remotecompose.player.platform.AndroidRemoteContext;
import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
/** A view to to display and play RemoteCompose documents */
@@ -114,6 +115,23 @@ public class RemoteComposePlayer extends FrameLayout implements RemoteContextAwa
return mInner.getDocument();
}
+ /**
+ * This will update values in the already loaded document.
+ *
+ * @param value the document to update variables in the current document width
+ */
+ public void updateDocument(RemoteComposeDocument value) {
+ RemoteComposeDocument document = value;
+ AndroidRemoteContext tmpContext = new AndroidRemoteContext();
+ document.initializeContext(tmpContext);
+ float density = getContext().getResources().getDisplayMetrics().density;
+ tmpContext.setAnimationEnabled(true);
+ tmpContext.setDensity(density);
+ tmpContext.setUseChoreographer(false);
+ mInner.getDocument().mDocument.applyUpdate(document.mDocument);
+ mInner.invalidate();
+ }
+
public void setDocument(RemoteComposeDocument value) {
if (value != null) {
if (value.canBeDisplayed(
@@ -312,7 +330,8 @@ public class RemoteComposePlayer extends FrameLayout implements RemoteContextAwa
}
/**
- * Add a callback for handling id actions events on the document
+ * Add a callback for handling id actions events on the document. Can only be added after the
+ * document has been loaded.
*
* @param callback the callback lambda that will be used when a action is executed
* <p>The parameter of the callback are:
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index ad2414974780..575a6b2ee518 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -22,7 +22,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
@@ -43,7 +42,6 @@ import java.util.HashMap;
*
* <p>This is used to play the RemoteCompose operations on Android.
*/
-@VisibleForTesting
public class AndroidRemoteContext extends RemoteContext {
public void useCanvas(Canvas canvas) {
@@ -197,7 +195,7 @@ public class AndroidRemoteContext extends RemoteContext {
@Override
public void runAction(int id, @NonNull String metadata) {
- mDocument.performClick(this, id);
+ mDocument.performClick(this, id, metadata);
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 29cd40def562..17f4fc82af5f 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -102,6 +102,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mDocument = value;
mDocument.initializeContext(mARContext);
mDisable = false;
+ mARContext.setDocLoadTime();
mARContext.setAnimationEnabled(true);
mARContext.setDensity(mDensity);
mARContext.setUseChoreographer(true);
@@ -154,7 +155,11 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
param.leftMargin = (int) area.getLeft();
param.topMargin = (int) area.getTop();
viewArea.setOnClickListener(
- view1 -> mDocument.getDocument().performClick(mARContext, area.getId()));
+ view1 ->
+ mDocument
+ .getDocument()
+ .performClick(
+ mARContext, area.getId(), area.getMetadata()));
addView(viewArea, param);
}
if (!clickAreas.isEmpty()) {
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index dec724b6a7ff..e1b3479c7ed2 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -558,8 +558,7 @@ static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr
delete parcel;
}
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
-{
+static Parcel* parcel_for_marshall(JNIEnv* env, jlong nativePtr) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL) {
return NULL;
@@ -577,6 +576,16 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na
return NULL;
}
+ return parcel;
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return NULL;
+ }
+
jbyteArray ret = env->NewByteArray(parcel->dataSize());
if (ret != NULL)
@@ -592,6 +601,56 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na
return ret;
}
+static long ensure_capacity(JNIEnv* env, Parcel* parcel, jint remaining) {
+ long dataSize = parcel->dataSize();
+ if (remaining < dataSize) {
+ jnihelp::ThrowException(env, "java/nio/BufferOverflowException", "()V");
+ return -1;
+ }
+ return dataSize;
+}
+
+static int android_os_Parcel_marshall_array(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint remaining)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+ if (array != NULL)
+ {
+ memcpy(array + offset, parcel->data(), data_size);
+ env->ReleasePrimitiveArrayCritical(data, array, 0);
+ }
+ return data_size;
+}
+
+static int android_os_Parcel_marshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint remaining) {
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer != NULL) {
+ memcpy(buffer + offset, parcel->data(), data_size);
+ }
+ return data_size;
+}
+
static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
jbyteArray data, jint offset, jint length)
{
@@ -613,6 +672,25 @@ static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong native
}
}
+static void android_os_Parcel_unmarshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint length)
+{
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel == NULL || length < 0) {
+ return;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer)
+ {
+ parcel->setDataSize(length);
+ parcel->setDataPosition(0);
+
+ void* raw = parcel->writeInplace(length);
+ memcpy(raw, (buffer + offset), length);
+ }
+}
+
static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
jlong otherNativePtr)
{
@@ -911,7 +989,10 @@ static const JNINativeMethod gParcelMethods[] = {
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
+ {"nativeMarshallArray", "(J[BII)I", (void*)android_os_Parcel_marshall_array},
+ {"nativeMarshallBuffer", "(JLjava/nio/ByteBuffer;II)I", (void*)android_os_Parcel_marshall_buffer},
{"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
+ {"nativeUnmarshallBuffer", "(JLjava/nio/ByteBuffer;II)V", (void*)android_os_Parcel_unmarshall_buffer},
{"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData},
{"nativeCompareDataInRange", "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
{"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index e874163943b6..a6a748c3deb7 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -55,41 +55,25 @@ public:
inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
- void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
void dispose(JNIEnv* env, jobject obj);
private:
std::shared_ptr<InputChannel> mInputChannel;
- InputChannelObjDisposeCallback mDisposeCallback;
- void* mDisposeData;
};
// ----------------------------------------------------------------------------
NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
- : mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
+ : mInputChannel(std::move(inputChannel)) {}
NativeInputChannel::~NativeInputChannel() {
}
-void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
- if (input_flags::remove_input_channel_from_windowstate()) {
- return;
- }
- mDisposeCallback = callback;
- mDisposeData = data;
-}
-
void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
if (!mInputChannel) {
return;
}
- if (mDisposeCallback) {
- mDisposeCallback(env, obj, mInputChannel, mDisposeData);
- mDisposeCallback = nullptr;
- mDisposeData = nullptr;
- }
mInputChannel.reset();
}
@@ -108,17 +92,6 @@ std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv*
return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
}
-void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
- InputChannelObjDisposeCallback callback, void* data) {
- NativeInputChannel* nativeInputChannel =
- android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
- if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
- ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
- } else {
- nativeInputChannel->setDisposeCallback(callback, data);
- }
-}
-
static jlong android_view_InputChannel_createInputChannel(
JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 7b085b16d24b..ba74b0e8cce5 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -109,27 +109,29 @@ public:
return binder::Status::ok();
}
captureResults.fenceResult.value()->waitForever(LOG_TAG);
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- jobject jGainmap = nullptr;
+ auto jhardwareBuffer = ScopedLocalRef<jobject>(
+ env, android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.buffer->toAHardwareBuffer()));
+ auto jGainmap = ScopedLocalRef<jobject>(env);
if (captureResults.optionalGainMap) {
- jGainmap = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.optionalGainMap->toAHardwareBuffer());
+ jGainmap = ScopedLocalRef<jobject>(
+ env, android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.optionalGainMap->toAHardwareBuffer()));
}
- jobject screenshotHardwareBuffer =
- env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+ auto screenshotHardwareBuffer =
+ ScopedLocalRef<jobject>(env, env->CallStaticObjectMethod(
+ gScreenshotHardwareBufferClassInfo.clazz,
gScreenshotHardwareBufferClassInfo.builder,
- jhardwareBuffer,
+ jhardwareBuffer.get(),
static_cast<jint>(captureResults.capturedDataspace),
captureResults.capturedSecureLayers,
- captureResults.capturedHdrLayers, jGainmap,
- captureResults.hdrSdrRatio);
+ captureResults.capturedHdrLayers, jGainmap.get(),
+ captureResults.hdrSdrRatio));
checkAndClearException(env, "builder");
- env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer,
+ env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept,
+ screenshotHardwareBuffer.get(),
fenceStatus(captureResults.fenceResult));
checkAndClearException(env, "accept");
- env->DeleteLocalRef(jhardwareBuffer);
- env->DeleteLocalRef(screenshotHardwareBuffer);
return binder::Status::ok();
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 59e01bfa0c4b..9df351fa39c4 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -35,7 +35,6 @@ import "frameworks/base/core/proto/android/os/system_properties.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarm/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -483,10 +482,8 @@ message IncidentProto {
(section).args = "connmetrics --proto"
];
- optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
- (section).type = SECTION_DUMPSYS,
- (section).args = "bluetooth_manager --proto"
- ];
+ // Deprecated BluetoothManagerServiceDumpProto
+ reserved 3050;
optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 1caa9e7af348..5831a0bfc39e 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -239,6 +239,10 @@ message SecureSettingsProto {
repeated SettingProto completed_categories = 15;
optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto adaptive_connectivity_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto adaptive_connectivity_wifi_enabled = 105 [ (android.privacy).dest =
+ DEST_AUTOMATIC ];
+ optional SettingProto adaptive_connectivity_mobile_network_enabled = 106 [ (android.privacy)
+ .dest = DEST_AUTOMATIC ];
message Controls {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -741,5 +745,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 105;
+ // Next tag = 107;
}
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 325790c22fce..8393f8b4db61 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -290,7 +290,16 @@ message SystemSettingsProto {
optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ message Display {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto cv_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Display display = 39;
+
+
+
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 39;
+ // Next tag = 40;
}
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
deleted file mode 100644
index 362daa73ff14..000000000000
--- a/core/proto/android/server/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// Copyright (C) 2021 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "srcs_bluetooth_manager_service_proto",
- srcs: [
- "bluetooth_manager_service.proto",
- ],
- visibility: ["//packages/modules/Bluetooth:__subpackages__"],
-}
-
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
deleted file mode 100644
index c33f66a9aeca..000000000000
--- a/core/proto/android/server/bluetooth_manager_service.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package com.android.server;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto";
-
-option java_multiple_files = true;
-
-message BluetoothManagerServiceDumpProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- message ActiveLog {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int64 timestamp_ms = 1;
- optional bool enable = 2;
- optional string package_name = 3;
- optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
- }
-
- optional bool enabled = 1;
- optional int32 state = 2;
- optional string state_name = 3;
- optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
- optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
- optional int64 last_enabled_time_ms = 6;
- optional int64 curr_timestamp_ms = 7;
- repeated ActiveLog active_logs = 8;
- optional int32 num_crashes = 9;
- optional bool crash_log_maxed = 10;
- repeated int64 crash_timestamps_ms = 11;
- optional int32 num_ble_apps = 12;
- repeated string ble_app_package_names = 13;
-} \ No newline at end of file
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index 902ef7fc38e8..615af6fb16f8 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -49,7 +49,8 @@
android:id="@+id/accessibility_autoclick_drag_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_drag"
- android:src="@drawable/accessibility_autoclick_drag" />
+ android:src="@drawable/accessibility_autoclick_drag"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -60,7 +61,8 @@
android:id="@+id/accessibility_autoclick_double_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_double_click"
- android:src="@drawable/accessibility_autoclick_double_click" />
+ android:src="@drawable/accessibility_autoclick_double_click"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -71,7 +73,8 @@
android:id="@+id/accessibility_autoclick_right_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_right_click"
- android:src="@drawable/accessibility_autoclick_right_click" />
+ android:src="@drawable/accessibility_autoclick_right_click"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -82,7 +85,8 @@
android:id="@+id/accessibility_autoclick_scroll_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_scroll"
- android:src="@drawable/accessibility_autoclick_scroll" />
+ android:src="@drawable/accessibility_autoclick_scroll"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -93,7 +97,8 @@
android:id="@+id/accessibility_autoclick_left_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_left_click"
- android:src="@drawable/accessibility_autoclick_left_click" />
+ android:src="@drawable/accessibility_autoclick_left_click"
+ android:clickable="false" />
</LinearLayout>
</LinearLayout>
@@ -114,7 +119,8 @@
android:id="@+id/accessibility_autoclick_pause_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_pause"
- android:src="@drawable/accessibility_autoclick_pause" />
+ android:src="@drawable/accessibility_autoclick_pause"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -125,7 +131,8 @@
android:id="@+id/accessibility_autoclick_position_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_position"
- android:src="@drawable/accessibility_autoclick_position" />
+ android:src="@drawable/accessibility_autoclick_position"
+ android:clickable="false" />
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_action_list.xml b/core/res/res/layout/notification_2025_action_list.xml
index 053aca068027..6c07ec1a7c7b 100644
--- a/core/res/res/layout/notification_2025_action_list.xml
+++ b/core/res/res/layout/notification_2025_action_list.xml
@@ -22,6 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/notification_2025_action_list_margin_bottom"
+ android:minHeight="@dimen/notification_2025_action_list_min_height"
>
<LinearLayout
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 7ec2450ceb71..16c95009051c 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -29,7 +29,8 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="@dimen/notification_2025_margin"
+ android:layout_marginHorizontal="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_2025_reply_history_container.xml b/core/res/res/layout/notification_2025_reply_history_container.xml
index 6923b59f34dc..256f7b89c784 100644
--- a/core/res/res/layout/notification_2025_reply_history_container.xml
+++ b/core/res/res/layout/notification_2025_reply_history_container.xml
@@ -28,16 +28,16 @@
android:layout_width="match_parent"
android:layout_height="1dip"
android:id="@+id/action_divider"
- android:layout_marginTop="@dimen/notification_content_margin"
- android:layout_marginBottom="@dimen/notification_content_margin"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:background="@drawable/notification_template_divider" />
<TextView
android:id="@+id/notification_material_reply_text_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
@@ -46,7 +46,7 @@
android:id="@+id/notification_material_reply_text_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
@@ -56,13 +56,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:layout_marginEnd="@dimen/notification_content_margin_end">
+ android:layout_marginEnd="@dimen/notification_2025_margin">
<TextView
android:id="@+id/notification_material_reply_text_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 63f32e3b3cd2..57c89b91cff7 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -74,7 +74,6 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
@@ -157,7 +156,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 4e0cf753722f..c57196e9c9e8 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -32,11 +32,11 @@
android:orientation="vertical"
>
- <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
+ android:layout_weight="1"
>
<ImageView
@@ -77,9 +77,7 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
android:orientation="vertical"
@@ -151,7 +149,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -170,23 +168,15 @@
android:layout_height="@dimen/notification_close_button_size"
android:layout_gravity="top|end" />
- </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+ </FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
- <include layout="@layout/notification_2025_action_list" />
- </LinearLayout>
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
+ <include layout="@layout/notification_2025_action_list" />
</LinearLayout>
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
index 5783201981a8..1c6fcb50dca5 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -32,12 +32,11 @@
android:orientation="vertical"
>
-
- <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
+ android:layout_weight="1"
>
<ImageView
@@ -78,17 +77,11 @@
android:clipChildren="false"
>
- <!--
- NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
- have the id/notification_headerless_view_column, as that is used for modifying
- vertical margins to accommodate the single-line state that base supports
- -->
<LinearLayout
+ android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:clipChildren="false"
@@ -150,7 +143,6 @@
android:layout_height="@dimen/notification_right_icon_size"
android:layout_gravity="center_vertical|end"
android:layout_marginTop="@dimen/notification_2025_margin"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
android:forceHasOverlappingRendering="false"
android:spacing="0dp"
@@ -175,7 +167,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -194,22 +186,15 @@
android:layout_height="@dimen/notification_close_button_size"
android:layout_gravity="top|end" />
- </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+ </FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
+
</LinearLayout>
-</LinearLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 629af77b3dda..de82f9feb512 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -76,7 +76,6 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
@@ -178,7 +177,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index 6391b1ebf744..8e2cb23f1198 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -35,11 +35,11 @@
>
- <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
+ android:layout_weight="1"
>
<ImageView
@@ -60,7 +60,8 @@
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_alignParentStart="true"
- android:layout_margin="@dimen/notification_2025_margin"
+ android:layout_marginHorizontal="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
@@ -88,17 +89,11 @@
android:clipChildren="false"
>
- <!--
- NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
- have the id/notification_headerless_view_column, as that is used for modifying
- vertical margins to accommodate the single-line state that base supports
- -->
<LinearLayout
+ android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:clipChildren="false"
@@ -160,7 +155,6 @@
android:layout_height="@dimen/notification_right_icon_size"
android:layout_gravity="center_vertical|end"
android:layout_marginTop="@dimen/notification_2025_margin"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
android:forceHasOverlappingRendering="false"
android:spacing="0dp"
@@ -185,7 +179,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -204,22 +198,15 @@
android:layout_height="@dimen/notification_close_button_size"
android:layout_gravity="top|end" />
- </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+ </FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
+
</LinearLayout>
-</LinearLayout>
</com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
index 52bc7b8ea3bb..b32a7788c433 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
@@ -76,7 +76,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
index be6404609f25..268396f11cab 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -103,7 +103,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml
index 76a85813b980..287110766dc7 100644
--- a/core/res/res/layout/notification_2025_template_expanded_base.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_base.xml
@@ -24,10 +24,8 @@
>
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
@@ -47,7 +45,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -78,7 +76,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
index 999afa66c65b..ead6d4cbc034 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -28,7 +28,6 @@
<include layout="@layout/notification_2025_right_icon" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
@@ -43,7 +42,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -67,7 +66,7 @@
android:layout_weight="1"
android:layout_marginTop="13dp"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:background="@drawable/notification_big_picture_outline"
android:clipToOutline="true"
android:scaleType="centerCrop"
@@ -85,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
index c9206eddbcde..de5e71d2015f 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -26,11 +26,9 @@
<include layout="@layout/notification_2025_template_header" />
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:clipToPadding="false"
android:orientation="vertical"
>
@@ -43,7 +41,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingStart="@dimen/notification_2025_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
+ android:paddingEnd="@dimen/notification_2025_margin"
android:clipToPadding="false"
android:orientation="vertical"
android:layout_weight="1"
@@ -84,7 +82,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index ec214554a30b..c096bc8a4d15 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -30,7 +30,6 @@
<include layout="@layout/notification_2025_conversation_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -46,7 +45,7 @@
android:layout_gravity="top"
android:layout_weight="1"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -62,7 +61,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
index 6ee82fa116ff..6eea8cc93aeb 100644
--- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -29,7 +29,6 @@
<include layout="@layout/notification_2025_conversation_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -44,7 +43,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -64,7 +63,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
index 1eaef228aaca..327cd7ac71bb 100644
--- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -24,7 +24,6 @@
>
<include layout="@layout/notification_2025_template_header" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
@@ -39,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingStart="@dimen/notification_2025_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
+ android:paddingEnd="@dimen/notification_2025_margin"
android:layout_weight="1"
android:clipToPadding="false"
android:orientation="vertical"
@@ -126,7 +125,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin" />
<include layout="@layout/notification_2025_action_list" />
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
index 801e339b3a92..565a558a5115 100644
--- a/core/res/res/layout/notification_2025_template_expanded_media.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -41,7 +41,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
<include layout="@layout/notification_template_part_line1"/>
@@ -53,7 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_media_actions_margin_start"
- android:minHeight="@dimen/notification_content_margin"
+ android:minHeight="@dimen/notification_2025_margin"
>
<!-- Nesting in FrameLayout is required to ensure that the marginStart actually applies
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 62059af7f056..df48479e5ec3 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -29,7 +29,6 @@
<include layout="@layout/notification_2025_template_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -44,7 +43,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -64,7 +63,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
index cf39d8b08c4f..b929b9ed20b7 100644
--- a/core/res/res/layout/notification_2025_template_expanded_progress.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -25,10 +25,8 @@
>
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
@@ -48,7 +46,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -114,7 +112,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
index 4d3b2453637d..e416c5054e00 100644
--- a/core/res/res/layout/notification_2025_template_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -56,7 +56,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7a38dce296de..e47adc90fc7a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2831,6 +2831,10 @@
<!-- Whether dreams are disabled when ambient mode is suppressed. -->
<bool name="config_dreamsDisabledByAmbientModeSuppressionConfig">false</bool>
+ <!-- The default for the setting that controls when to auto-start hub mode.
+ 0 means "never" -->
+ <integer name="config_whenToStartHubModeDefault">0</integer>
+
<!-- The duration in milliseconds of the dream opening animation. -->
<integer name="config_dreamOpenAnimationDuration">250</integer>
<!-- The duration in milliseconds of the dream closing animation. -->
@@ -6108,6 +6112,9 @@
<!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
<bool name="config_expandLockScreenUserSwitcher">false</bool>
+ <!-- Help URI, action disabled by advanced protection [DO NOT TRANSLATE] -->
+ <string name="config_help_url_action_disabled_by_advanced_protection" translatable="false"></string>
+
<!-- Toasts posted from these packages will be shown to the current user, regardless of the user
the process belongs to. This is useful for packages that run under a single user but serve
multiple users, e.g. the system.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6e540833bc46..b5ed28f82dda 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -295,6 +295,11 @@
<!-- The margin of the notification action list at the bottom in the 2025 redesign -->
<dimen name="notification_2025_action_list_margin_bottom">6dp</dimen>
+ <!-- The minimum height of the notification action container, to act as a bottom padding for the
+ notification when there are no actions. This should always be equal to
+ notification_2025_margin - notification_2025_action_list_margin_bottom. -->
+ <dimen name="notification_2025_action_list_min_height">10dp</dimen>
+
<!-- The overall height of the emphasized notification action -->
<dimen name="notification_action_emphasized_height">48dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 922d59dfcfe0..93fe4085e843 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2072,6 +2072,7 @@
<java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
<java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
<java-symbol type="bool" name="config_glanceableHubEnabled" />
+ <java-symbol type="integer" name="config_whenToStartHubModeDefault" />
<java-symbol type="integer" name="config_keyguardDrawnTimeout" />
<java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
@@ -5963,6 +5964,8 @@
<!-- Device CMF Theming Settings -->
<java-symbol type="array" name="theming_defaults" />
+ <java-symbol type="string" name="config_help_url_action_disabled_by_advanced_protection" />
+
<!-- Advanced Protection Service USB feature -->
<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_title" />
<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_text" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index fa0744775f6d..5af837fce612 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -18,7 +18,7 @@ package com.android.server.broadcastradio.hal2;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static org.junit.Assert.*;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index e8b295bd5fdb..0287e6c086aa 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.usb.UsbDevice;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -118,6 +118,6 @@ public class BrailleDisplayControllerImplTest {
verify(mBrailleDisplayCallback).onConnectionFailed(
BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
- verifyZeroInteractions(mAccessibilityServiceConnection);
+ verifyNoMoreInteractions(mAccessibilityServiceConnection);
}
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index dccbf4036b3e..70b4150115fe 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -233,7 +233,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
/**
* Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
*/
- private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+ public class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
static final String ATTRIBUTES_NAME = "test";
@@ -245,12 +245,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
}
- TestServicesCache(Injector<TestServiceType> injector,
- XmlSerializerAndParser<TestServiceType> serializerAndParser) {
- super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
- serializerAndParser);
- }
-
@Override
public TestServiceType parseServiceAttributes(Resources res, String packageName,
AttributeSet attrs) {
@@ -338,7 +332,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
}
}
- static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
+ public static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
out.attribute(null, "type", item.type);
@@ -353,7 +347,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
}
}
- static class TestServiceType implements Parcelable {
+ public static class TestServiceType implements Parcelable {
final String type;
final String value;
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
new file mode 100644
index 000000000000..8349659517c5
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.Flags.FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.RegisteredServicesCacheTest.TestServiceType;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.content.pm.RegisteredServicesCache}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE)
+public class RegisteredServicesCacheUnitTest {
+ private static final String TAG = "RegisteredServicesCacheUnitTest";
+ private static final int U0 = 0;
+ private static final int U1 = 1;
+ private static final int UID1 = 1;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private final ResolveInfo mResolveInfo1 = new ResolveInfo();
+ private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+ private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
+ private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
+ @Mock
+ RegisteredServicesCache.Injector<TestServiceType> mMockInjector;
+ @Mock
+ Context mMockContext;
+ Handler mMockBackgroundHandler;
+ @Mock
+ PackageManager mMockPackageManager;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockInjector.getContext()).thenReturn(mMockContext);
+ mMockBackgroundHandler = spy(BackgroundThread.getHandler());
+ when(mMockInjector.getBackgroundHandler()).thenReturn(mMockBackgroundHandler);
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiverAsUser(any(), any(), any(),
+ any(), any());
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiver(any(), any(), any(),
+ any());
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+ addServiceInfoIntoResolveInfo(mResolveInfo1, "r1.package.name" /* packageName */,
+ "r1.service.name" /* serviceName */);
+ addServiceInfoIntoResolveInfo(mResolveInfo2, "r2.package.name" /* packageName */,
+ "r2.service.name" /* serviceName */);
+ }
+
+ @Test
+ public void testSaveServiceInfoIntoCaches() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ int u1uid = UserHandle.getUid(U1, UID1);
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+ 2000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterRemoveUserId() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.onUserRemoved(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ @Test
+ public void testGetServiceInfoCachesForMultiUser() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.clearServicesForQuerying();
+ int u1uid = UserHandle.getUid(U1, UID1);
+ assertThat(u1uid).isNotEqualTo(UID1);
+
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
+
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+
+ // There is a bug to return the same info from the cache for different users. Make sure it
+ // will return the different info from the cache for different users.
+ Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> serviceInfos;
+ serviceInfos = testServicesCache.getAllServices(U0);
+ // Make sure the service info is retrieved from the cache for U0.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(UID1);
+ }
+
+ serviceInfos = testServicesCache.getAllServices(U1);
+ // Make sure the service info is retrieved from the cache for U1.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(u1uid);
+ }
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenPackageInfoNotFound() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ doThrow(new SecurityException("")).when(mMockPackageManager).getPackageInfoAsUser(
+ eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0));
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), anyLong());
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenTheApplicationHasBeenUpdated() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo2);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ // Immediately invoke run on the Runnable posted to the handler
+ doAnswer(invocation -> {
+ Message message = invocation.getArgument(0);
+ message.getCallback().run();
+ return true;
+ }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+ TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
+ final ComponentInfo info = new ComponentInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.uid = uid;
+ return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
+ }
+
+ private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+ String serviceName) {
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = serviceName;
+ resolveInfo.serviceInfo = serviceInfo;
+ }
+
+ private PackageInfo createPackageInfo(long lastUpdateTime) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.lastUpdateTime = lastUpdateTime;
+ return packageInfo;
+ }
+
+ /**
+ * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
+ */
+ public class TestRegisteredServicesCache extends RegisteredServicesCache<TestServiceType> {
+ static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
+ static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
+ static final String ATTRIBUTES_NAME = "test";
+ private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+ new SparseArray<>();
+
+ public TestRegisteredServicesCache(Injector<TestServiceType> injector,
+ XmlSerializerAndParser<TestServiceType> serializerAndParser) {
+ super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
+ serializerAndParser);
+ }
+
+ @Override
+ public TestServiceType parseServiceAttributes(Resources res, String packageName,
+ AttributeSet attrs) {
+ return null;
+ }
+
+ @Override
+ protected List<ResolveInfo> queryIntentServices(int userId) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+ new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+ return new ArrayList<>(map.keySet());
+ }
+
+ void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+ ServiceInfo<TestServiceType> serviceInfo) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+ if (map == null) {
+ map = new HashMap<>();
+ mServices.put(userId, map);
+ }
+ map.put(resolveInfo, serviceInfo);
+ }
+
+ void clearServicesForQuerying() {
+ mServices.clear();
+ }
+
+ @Override
+ protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
+ long lastUpdateTime) throws XmlPullParserException, IOException {
+ int size = mServices.size();
+ for (int i = 0; i < size; i++) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+ ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
+ if (serviceInfo != null) {
+ return serviceInfo;
+ }
+ }
+ throw new IllegalArgumentException("Unexpected service " + resolveInfo);
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ super.onUserRemoved(userId);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
index 8b513cb996b5..524e35535f03 100644
--- a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
@@ -136,9 +136,11 @@ public class SystemFeaturesCacheTest {
SystemFeaturesCache cache = new SystemFeaturesCache(features);
SystemFeaturesCache.clearInstance();
+ assertThat(SystemFeaturesCache.hasInstance()).isFalse();
assertThrows(IllegalStateException.class, () -> SystemFeaturesCache.getInstance());
SystemFeaturesCache.setInstance(cache);
+ assertThat(SystemFeaturesCache.hasInstance()).isTrue();
assertThat(SystemFeaturesCache.getInstance()).isEqualTo(cache);
assertThrows(
@@ -149,6 +151,7 @@ public class SystemFeaturesCacheTest {
@Test
public void testSingletonAutomaticallySetWithFeatureEnabled() {
assumeTrue(android.content.pm.Flags.cacheSdkSystemFeatures());
+ assertThat(SystemFeaturesCache.hasInstance()).isTrue();
assertThat(SystemFeaturesCache.getInstance()).isNotNull();
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index de5f0ffbe23f..34650be331d3 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -264,7 +264,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -272,7 +272,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -280,7 +280,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
}
@Test
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 46f22cec4213..504786111efe 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -20,8 +20,8 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.fail;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import android.Manifest.permission;
import android.content.Context;
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 3e6520106ab0..bb059108d4b6 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,6 +29,8 @@ import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -416,4 +418,63 @@ public class ParcelTest {
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
+
+ private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
+
+ // Allow for some Parcel overhead
+ private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
+
+ @Test
+ public void testMarshall_ByteBuffer_wrapped() {
+ ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ @Test
+ public void testMarshall_DirectByteBuffer() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ private void testMarshall_ByteBuffer(ByteBuffer bb) {
+ // Ensure that Parcel respects the starting offset by not starting at 0
+ bb.position(1);
+ bb.mark();
+
+ // Parcel test data, then marshall into the ByteBuffer
+ Parcel p1 = Parcel.obtain();
+ p1.writeByteArray(TEST_DATA);
+ p1.marshall(bb);
+ p1.recycle();
+
+ assertTrue(bb.position() > 1);
+ bb.reset();
+
+ // Unmarshall test data into a new Parcel
+ Parcel p2 = Parcel.obtain();
+ bb.reset();
+ p2.unmarshall(bb);
+ assertTrue(bb.position() > 1);
+ p2.setDataPosition(0);
+ byte[] marshalled = p2.marshall();
+
+ bb.reset();
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(bb.get(), marshalled[i]);
+ }
+
+ byte[] testDataCopy = new byte[TEST_DATA.length];
+ p2.setDataPosition(0);
+ p2.readByteArray(testDataCopy);
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(TEST_DATA[i], testDataCopy[i]);
+ }
+
+ // Test that overflowing the buffer throws an exception
+ bb.reset();
+ // Leave certainly not enough room for the test data
+ bb.limit(bb.position() + TEST_DATA.length - 1);
+ assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
+ p2.recycle();
+ }
}
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
index 21a22057d7c8..f5209952508c 100644
--- a/core/tests/coretests/src/android/provider/FontsContractTest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -21,8 +21,8 @@ import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABL
import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 8ac9292390b0..50cd4c00c2ce 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.CancellationSignal;
@@ -230,7 +230,7 @@ public class PendingInsetsControllerTest {
InsetsController secondController = mock(InsetsController.class);
mPendingInsetsController.replayAndAttach(secondController);
verify(mReplayedController).show(eq(systemBars()));
- verifyZeroInteractions(secondController);
+ verifyNoMoreInteractions(secondController);
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index e5ad5613af2d..341947c900c5 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -29,8 +29,8 @@ import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -79,7 +79,7 @@ public class AccessibilityCacheTest {
@Before
public void setUp() {
mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
- when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
+ when(mAccessibilityNodeRefresher.refreshNode(any(), anyBoolean())).thenReturn(true);
mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
}
@@ -854,7 +854,7 @@ public class AccessibilityCacheTest {
try {
assertEventTypeClearsNode(eventType, false);
verify(mAccessibilityNodeRefresher, never())
- .refreshNode(anyObject(), anyBoolean());
+ .refreshNode(any(), anyBoolean());
} catch (Throwable e) {
throw new AssertionError(
"Failed for eventType: " + AccessibilityEvent.eventTypeToString(
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index eb482f2e0aa5..f811d8efedeb 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.MockitoAnnotations.initMocks;
import android.os.Bundle;
@@ -74,7 +74,7 @@ public class AccessibilityInteractionClientTest {
MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
assertEquals("Node got lost along the way", nodeFromConnection, node);
- verifyZeroInteractions(mMockCache);
+ verifyNoMoreInteractions(mMockCache);
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 82e34275c66c..551357c6ba7a 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -34,8 +34,8 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 5f89f9c14793..8bbe81da512b 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -27,7 +27,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -130,7 +130,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -151,7 +151,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -172,7 +172,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -197,7 +197,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -227,7 +227,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNotNull();
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -255,7 +255,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -272,8 +272,8 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -289,8 +289,8 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -307,7 +307,7 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -325,7 +325,7 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -339,7 +339,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
verify(mMockSystemServerInterface).finishSession(anyInt());
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -352,8 +352,8 @@ public class MainContentCaptureSessionTest {
session.resetSession(/* newState= */ 0);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockSystemServerInterface);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockSystemServerInterface);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -370,8 +370,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -388,8 +388,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -407,8 +407,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index ba0dbf454ad2..e75452cafdab 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -443,7 +442,7 @@ public class ContentProtectionEventProcessorTest {
mTestLooper.dispatchAll();
verify(mMockEventBuffer, never()).clear();
verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ verifyNoMoreInteractions(mMockContentCaptureManager);
}
private void assertLoginDetected() throws Exception {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 3eb7d9aa738a..34ccc8bb5179 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -51,7 +51,7 @@ import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index b61d86819c17..3570c2e0ace0 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -33,7 +33,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
@@ -159,7 +158,7 @@ public class TextViewReceiveContentTest {
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
@Test
@@ -180,19 +179,19 @@ public class TextViewReceiveContentTest {
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
private static class MyInputConnection extends InputConnectionWrapper {
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index 381b566018c7..ad68e385459e 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@ class BackTouchTrackerTest {
fun generatesProgress_onStart() {
val linearTracker = linearTouchTracker()
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- val event = linearTracker.createStartEvent(null)
+ val event = linearTracker.createStartEvent()
assertEquals(0f, event.progress, 0f)
}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 215c1623a530..66524d1c1d2a 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -695,8 +695,7 @@ public class WindowOnBackInvokedDispatcherTest {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 74b4de1833ea..1977ff52c7c5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHO
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
@@ -34,17 +35,16 @@ import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -165,7 +165,7 @@ public class AccessibilityShortcutControllerTest {
.thenReturn(accessibilityManager);
when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext))
.thenReturn(mAlertDialogBuilder);
- when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
+ when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), any(), anyInt()))
.thenReturn(mToast);
when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext);
when(mFrameworkObjectProvider.getTextToSpeech(eq(mContext), any()))
@@ -179,20 +179,20 @@ public class AccessibilityShortcutControllerTest {
ResolveInfo resolveInfo = mock(ResolveInfo.class);
resolveInfo.serviceInfo = mock(ServiceInfo.class);
resolveInfo.serviceInfo.applicationInfo = mApplicationInfo;
- when(resolveInfo.loadLabel(anyObject())).thenReturn(PACKAGE_NAME_STRING);
+ when(resolveInfo.loadLabel(any())).thenReturn(PACKAGE_NAME_STRING);
when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
when(mServiceInfo.getComponentName())
.thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
when(mServiceInfo.loadSummary(any())).thenReturn(SERVICE_NAME_SUMMARY);
- when(mAlertDialogBuilder.setTitle(anyObject())).thenReturn(mAlertDialogBuilder);
+ when(mAlertDialogBuilder.setTitle(any())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
- when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder);
- when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject()))
+ when(mAlertDialogBuilder.setMessage(any())).thenReturn(mAlertDialogBuilder);
+ when(mAlertDialogBuilder.setPositiveButton(anyInt(), any()))
.thenReturn(mAlertDialogBuilder);
- when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject()))
+ when(mAlertDialogBuilder.setNegativeButton(anyInt(), any()))
.thenReturn(mAlertDialogBuilder);
- when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder);
+ when(mAlertDialogBuilder.setOnCancelListener(any())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog);
mLayoutParams.privateFlags = 0;
@@ -348,7 +348,7 @@ public class AccessibilityShortcutControllerTest {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
AccessibilityShortcutController accessibilityShortcutController = getController();
accessibilityShortcutController.performAccessibilityShortcut();
- verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject());
+ verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), any());
}
@Test
@@ -522,7 +522,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+ verifyNoMoreInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
@@ -615,7 +615,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@@ -632,7 +632,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@@ -715,6 +715,25 @@ public class AccessibilityShortcutControllerTest {
verify(mRingtone, times(0)).play();
}
+ @Test
+ public void onUserSetupComplete_noEnabledServices_blankHardwareSetting() throws Exception {
+ AccessibilityShortcutController controller = getController();
+ configureValidShortcutService();
+ // Shortcut setting should be cleared on user setup
+ Settings.Secure.putStringForUser(
+ mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, 0);
+ when(mAccessibilityManagerService
+ .getEnabledAccessibilityServiceList(anyInt(), eq(0)))
+ .thenReturn(Collections.emptyList());
+ Settings.Secure.putInt(mContentResolver, USER_SETUP_COMPLETE, 1);
+
+ controller.mUserSetupCompleteObserver.onChange(true);
+
+ final String shortcut = Settings.Secure.getStringForUser(
+ mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 0);
+ assertThat(shortcut).isEqualTo("");
+ }
+
private void configureNoShortcutService() throws Exception {
when(mAccessibilityManagerService
.getAccessibilityShortcutTargets(HARDWARE))
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index d21ab44d251d..15e746cb13b6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -87,6 +87,10 @@ public class ResolverActivityTest {
private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
.getInstrumentation().getTargetContext().getUser();
+ private static final int WORK_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 1;
+ private static final int CLONE_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 2;
+ private static final int PRIVATE_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 3;
+
@Rule
public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
new ActivityTestRule<>(ResolverWrapperActivity.class, false,
@@ -247,7 +251,7 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
PERSONAL_USER_HANDLE);
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -270,7 +274,7 @@ public class ResolverActivityTest {
};
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10,
+ createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
PERSONAL_USER_HANDLE);
// We pick the first one as there is another one in the work profile side
onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
@@ -444,7 +448,7 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -456,7 +460,7 @@ public class ResolverActivityTest {
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
waitForIdle();
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
+ assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
// The work list adapter must be populated in advance before tapping the other tab
assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
@@ -466,7 +470,7 @@ public class ResolverActivityTest {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -478,7 +482,7 @@ public class ResolverActivityTest {
waitForIdle();
onView(withText(R.string.resolver_work_tab)).perform(click());
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(WORK_USER_ID));
assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
@@ -498,7 +502,7 @@ public class ResolverActivityTest {
waitForIdle();
onView(withText(R.string.resolver_work_tab)).perform(click());
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(WORK_USER_ID));
assertThat(activity.getPersonalListAdapter().getCount(), is(2));
}
@@ -508,7 +512,7 @@ public class ResolverActivityTest {
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
sOverrides.workProfileUserHandle);
@@ -530,7 +534,7 @@ public class ResolverActivityTest {
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
sOverrides.workProfileUserHandle);
@@ -633,7 +637,7 @@ public class ResolverActivityTest {
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
sOverrides.workProfileUserHandle);
@@ -669,7 +673,7 @@ public class ResolverActivityTest {
markWorkProfileUserAvailable();
int workProfileTargets = 4;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets,
@@ -697,7 +701,7 @@ public class ResolverActivityTest {
markWorkProfileUserAvailable();
int workProfileTargets = 4;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets,
@@ -844,7 +848,7 @@ public class ResolverActivityTest {
public void testAutolaunch_singleTarget_withWorkProfileAndTabbedViewOff_noAutolaunch() {
ResolverActivity.ENABLE_TABBED_VIEW = false;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
PERSONAL_USER_HANDLE);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
@@ -898,7 +902,7 @@ public class ResolverActivityTest {
markWorkProfileUserAvailable();
int workProfileTargets = 4;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets,
@@ -1376,15 +1380,16 @@ public class ResolverActivityTest {
}
private void markWorkProfileUserAvailable() {
- ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
+ ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(WORK_USER_ID);
}
private void markCloneProfileUserAvailable() {
- ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(11);
+ ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(CLONE_USER_ID);
}
private void markPrivateProfileUserAvailable() {
- ResolverWrapperActivity.sOverrides.privateProfileUserHandle = UserHandle.of(12);
+ ResolverWrapperActivity.sOverrides.privateProfileUserHandle =
+ UserHandle.of(PRIVATE_USER_ID);
}
private void setTabOwnerUserHandleForLaunch(UserHandle tabOwnerUserHandleForLaunch) {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 90f5c24e13a1..cdf506c941e9 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -22,10 +22,10 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.intThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 4604b01d1bd2..050a68a89d6f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -112,8 +112,8 @@ public class ResolverWrapperActivity extends ResolverActivity {
@Override
protected ResolverListController createListController(UserHandle userHandle) {
- if (userHandle == UserHandle.SYSTEM) {
- when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+ if (userHandle == getUser()) {
+ when(sOverrides.resolverListController.getUserHandle()).thenReturn(getUser());
return sOverrides.resolverListController;
}
if (isLaunchedInSingleUserMode()) {
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 17fe15c94294..21ef391ee9de 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.EnforcePermission;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -207,7 +207,7 @@ public final class DeviceStateManagerGlobalTest {
mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
mService.setBaseState(OTHER_DEVICE_STATE);
- verifyZeroInteractions(callback);
+ verifyNoMoreInteractions(callback);
}
@Test
diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
index 8de919681006..7c6046223698 100644
--- a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
+++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
@@ -22,10 +22,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.contains;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.matches;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 3b4014867ef7..f25ceb14fb10 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -139,4 +139,10 @@ to pre-existing users, but cannot uninstall pre-existing system packages from pr
<install-in-user-type package="com.android.multiuser">
<install-in user-type="FULL" />
</install-in-user-type>
+
+ <!-- PrivateSpace App, only install in private profile -->
+ <install-in-user-type package="com.android.privatespace">
+ <install-in user-type="android.os.usertype.profile.PRIVATE" />
+ </install-in-user-type>
+
</config>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd5a54c2fd3f..a4ba2b398deb 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -755,6 +755,9 @@ public final class Bitmap implements Parcelable {
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
b.mDensity = mDensity;
+ if (hasGainmap()) {
+ b.setGainmap(getGainmap().asShared());
+ }
}
return b;
}
@@ -767,7 +770,8 @@ public final class Bitmap implements Parcelable {
*/
@NonNull
public Bitmap asShared() {
- if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) {
+ if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)
+ && (!hasGainmap() || getGainmap().asShared() == getGainmap())) {
return this;
}
Bitmap shared = createAshmemBitmap();
@@ -2091,7 +2095,7 @@ public final class Bitmap implements Parcelable {
*/
public void setGainmap(@Nullable Gainmap gainmap) {
checkRecycled("Bitmap is recycled");
- mGainmap = null;
+ mGainmap = gainmap;
nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr);
}
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 7fc13db85659..2417a1270bc5 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -161,6 +161,18 @@ public final class Gainmap implements Parcelable {
}
/**
+ * @hide
+ */
+ public Gainmap asShared() {
+ final Bitmap sharedContents = mGainmapContents.asShared();
+ if (sharedContents == mGainmapContents) {
+ return this;
+ } else {
+ return new Gainmap(sharedContents, nCreateCopy(mNativePtr));
+ }
+ }
+
+ /**
* @return Returns the image data of the gainmap represented as a Bitmap. This is represented
* as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored
* such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 592c7cdd070c..b6a1501831c0 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -114,7 +114,7 @@ flag {
name: "enable_shell_top_task_tracking"
namespace: "multitasking"
description: "Enables tracking top tasks from the shell"
- bug: "342627272"
+ bug: "346588978"
metadata {
purpose: PURPOSE_BUGFIX
}
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 4daaf9c6b57f..225303b2d942 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -103,4 +103,35 @@
</LinearLayout>
+ <!-- Menu option to move a bubble to fullscreen; only visible if bubble anything is enabled. -->
+ <LinearLayout
+ android:id="@+id/bubble_manage_menu_fullscreen_container"
+ android:background="@drawable/bubble_manage_menu_row"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:minHeight="@dimen/bubble_menu_item_height"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/bubble_manage_menu_fullscreen_icon"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
+ android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+ android:tint="@color/bubbles_icon_tint"/>
+
+ <TextView
+ android:id="@+id/bubble_manage_menu_fullscreen_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:text="@string/bubble_fullscreen_text"
+ android:textColor="@androidprv:color/materialColorOnSurface"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index e11babe5cb0e..bfaa40771894 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,8 +22,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
- android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
+ android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
+ android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
android:orientation="vertical">
<LinearLayout
@@ -43,8 +43,7 @@
android:layout_height="@dimen/desktop_mode_handle_menu_icon_radius"
android:layout_marginStart="10dp"
android:layout_marginEnd="12dp"
- android:contentDescription="@string/app_icon_text"
- android:importantForAccessibility="no"/>
+ android:contentDescription="@string/app_icon_text" />
<com.android.wm.shell.windowdecor.MarqueedTextView
android:id="@+id/application_name"
@@ -142,7 +141,6 @@
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -150,7 +148,6 @@
android:contentDescription="@string/new_window_text"
android:text="@string/new_window_text"
android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -158,7 +155,6 @@
android:contentDescription="@string/manage_windows_text"
android:text="@string/manage_windows_text"
android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -166,7 +162,6 @@
android:contentDescription="@string/change_aspect_ratio_text"
android:text="@string/change_aspect_ratio_text"
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
@@ -186,7 +181,6 @@
android:text="@string/open_in_browser_text"
android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
style="@style/DesktopModeHandleMenuActionButton"
- android:importantForAccessibility="no"
android:layout_width="0dp"
android:layout_weight="1"/>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
index de38e6fc2330..35e7de0e7c1e 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -22,8 +22,7 @@
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:paddingHorizontal="16dp"
- android:clickable="true"
- android:focusable="true"
+ android:importantForAccessibility="yes"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9ebbf71138b0..e1bf6638a9b2 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -291,6 +291,9 @@
<!-- Corner radius for expanded view drop target -->
<dimen name="bubble_bar_expanded_view_drop_target_corner">28dp</dimen>
<dimen name="bubble_bar_expanded_view_drop_target_padding">24dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen>
<!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
<dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
<!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
@@ -525,17 +528,21 @@
<!-- The radius of the Maximize menu shadow. -->
<dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
- <!-- The width of the handle menu in desktop mode. -->
- <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
+ <!-- The width of the handle menu in desktop mode plus the 2dp added for padding to account for
+ pill elevation. -->
+ <dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
- additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
- 52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
- <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
+ additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
+ plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
+ <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
+ <!-- The padding added to account for the handle menu's pills' elevation. -->
+ <dimen name="desktop_mode_handle_menu_pill_elevation_padding">2dp</dimen>
+
<!-- The height of the handle menu's "App Info" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 637b47ab3ace..5f1db83d7acb 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -45,6 +45,7 @@
<item name="android:layout_height">52dp</item>
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:importantForAccessibility">no</item>
</style>
<style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index 9ea0532f9450..529203f7ded2 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -19,13 +19,11 @@ package com.android.wm.shell.shared.desktopmode
import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.content.Context
-import android.content.pm.ActivityInfo
-import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED
-import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION
-import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
import android.content.pm.PackageManager
import android.window.DesktopModeFlags
import com.android.internal.R
+import com.android.internal.policy.DesktopModeCompatUtils
+import java.util.function.Supplier
/**
* Class to decide whether to apply app compat policies in desktop mode.
@@ -37,9 +35,11 @@ class DesktopModeCompatPolicy(private val context: Context) {
private val pkgManager: PackageManager
get() = context.getPackageManager()
private val defaultHomePackage: String?
- get() = pkgManager.getHomeActivities(ArrayList())?.packageName
+ get() = defaultHomePackageSupplier?.get()
+ ?: pkgManager.getHomeActivities(ArrayList())?.packageName
private val packageInfoCache = mutableMapOf<String, Boolean>()
+ var defaultHomePackageSupplier: Supplier<String?>? = null
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -49,33 +49,28 @@ class DesktopModeCompatPolicy(private val context: Context) {
*/
fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
- task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+ task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
+ task.userId)
- fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
- numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+ fun isTopActivityExemptFromDesktopWindowing(
+ packageName: String?,
+ numActivities: Int,
+ isTopActivityNoDisplay: Boolean,
+ isActivityStackTransparent: Boolean,
+ userId: Int
+ ) =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
((isSystemUiTask(packageName) ||
isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
(isTransparentTask(isActivityStackTransparent, numActivities) &&
- hasFullscreenTransparentPermission(packageName))) &&
+ hasFullscreenTransparentPermission(packageName, userId))) &&
!isTopActivityNoDisplay)
- /**
- * Whether the caption insets should be excluded from configuration for system to handle.
- *
- * The treatment is enabled when all the of the following is true:
- * * Any flags to forcibly consume caption insets are enabled.
- * * Top activity have configuration coupled with insets.
- * * Task is not resizeable or [ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS]
- * is enabled.
- */
+ /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean =
- DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue
- && isAnyForceConsumptionFlagsEnabled()
- && taskInfo.topActivityInfo?.let {
- isInsetsCoupledWithConfiguration(it) && (!taskInfo.isResizeable || it.isChangeEnabled(
- OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
- ))
+ taskInfo.topActivityInfo?.let {
+ DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds(it, taskInfo.isResizeable,
+ taskInfo.appCompatTaskInfo.hasOptOutEdgeToEdge())
} ?: false
/**
@@ -91,16 +86,17 @@ class DesktopModeCompatPolicy(private val context: Context) {
private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
// Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
- private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+ private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
if (packageName == null) {
return false
}
- return packageInfoCache.getOrPut(packageName) {
+ return packageInfoCache.getOrPut("$userId@$packageName") {
try {
- val packageInfo = pkgManager.getPackageInfo(
+ val packageInfo = pkgManager.getPackageInfoAsUser(
packageName,
- PackageManager.GET_PERMISSIONS
+ PackageManager.GET_PERMISSIONS,
+ userId
)
packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
} catch (e: PackageManager.NameNotFoundException) {
@@ -118,12 +114,4 @@ class DesktopModeCompatPolicy(private val context: Context) {
*/
private fun isPartOfDefaultHomePackageOrNoHomeAvailable(packageName: String?) =
defaultHomePackage == null || (packageName != null && packageName == defaultHomePackage)
-
- private fun isAnyForceConsumptionFlagsEnabled(): Boolean =
- DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue
- || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue
-
- private fun isInsetsCoupledWithConfiguration(info: ActivityInfo): Boolean =
- !(info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
- || info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED))
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 53dede6bd227..7f8cfaeb9c03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -448,7 +448,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
- tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -604,7 +604,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
if (!isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -1041,7 +1041,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
() -> mShellExecutor.execute(this::onBackAnimationFinished));
if (mApps.length >= 1) {
- BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
+ BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
dispatchOnBackStarted(mActiveCallback, startEvent);
if (startEvent.getSwipeEdge() == EDGE_NONE) {
// TODO(b/373544911): onBackStarted is dispatched here so that
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 efc952644f0b..912de813cf59 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
@@ -2644,7 +2644,8 @@ public class BubbleController implements ConfigurationChangeListener,
}
private void moveBubbleToFullscreen(String key) {
- // TODO b/388858013: convert the bubble to full screen
+ Bubble b = mBubbleData.getBubbleInStackWithKey(key);
+ mBubbleTransitions.startDraggedBubbleIconToFullscreen(b);
}
private boolean isDeviceLocked() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 8ac9230c36c3..cbd1e9671825 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -606,6 +606,10 @@ public class BubbleExpandedView extends LinearLayout {
updateManageButtonIfExists();
}
+ public float getCornerRadius() {
+ return mCornerRadius;
+ }
+
/**
* Updates the size and visuals of the pointer if {@link #mPointerView} is initialized.
* Does nothing otherwise.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 70340d7032b4..03d6b0a8075d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -103,7 +103,9 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
private int mManageButtonHeight;
private int mOverflowHeight;
private int mMinimumFlyoutWidthLargeScreen;
- private int mBubbleBarExpandedViewDropTargetPadding;
+ private int mBarExpViewDropTargetPaddingTop;
+ private int mBarExpViewDropTargetPaddingBottom;
+ private int mBarExpViewDropTargetPaddingHorizontal;
private PointF mRestingStackPosition;
@@ -173,8 +175,12 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
mPositionRect.width() - 2 * mExpandedViewPadding
);
- mBubbleBarExpandedViewDropTargetPadding = res.getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_drop_target_padding);
+ mBarExpViewDropTargetPaddingTop = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_top);
+ mBarExpViewDropTargetPaddingBottom = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom);
+ mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal);
if (mShowingInBubbleBar) {
mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
@@ -986,8 +992,15 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
public Rect getBubbleBarExpandedViewDropTargetBounds(boolean onLeft) {
Rect bounds = new Rect();
getBubbleBarExpandedViewBounds(onLeft, false, bounds);
- bounds.inset(mBubbleBarExpandedViewDropTargetPadding,
- mBubbleBarExpandedViewDropTargetPadding);
+ // Drop target bounds are based on expanded view bounds with some padding added
+ int leftPadding = onLeft ? 0 : mBarExpViewDropTargetPaddingHorizontal;
+ int rightPadding = onLeft ? mBarExpViewDropTargetPaddingHorizontal : 0;
+ bounds.inset(
+ leftPadding,
+ mBarExpViewDropTargetPaddingTop,
+ rightPadding,
+ mBarExpViewDropTargetPaddingBottom
+ );
return bounds;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 92724178cf84..dd5a23aae7f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -91,6 +91,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -1319,7 +1320,7 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.bringToFront();
}
- // TODO: Create ManageMenuView and move setup / animations there
+ // TODO (b/402196554) : Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
removeView(mManageMenu);
@@ -1377,6 +1378,22 @@ public class BubbleStackView extends FrameLayout
mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
+ View fullscreenView = mManageMenu.findViewById(
+ R.id.bubble_manage_menu_fullscreen_container);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ fullscreenView.setVisibility(VISIBLE);
+ fullscreenView.setOnClickListener(
+ view -> {
+ showManageMenu(false /* show */);
+ BubbleExpandedView expandedView = getExpandedView();
+ if (expandedView != null && expandedView.getTaskView() != null) {
+ expandedView.getTaskView().moveToFullscreen();
+ }
+ });
+ } else {
+ fullscreenView.setVisibility(GONE);
+ }
+
// The menu itself should respect locale direction so the icons are on the correct side.
mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 51a5b12edb84..c1841c707a2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -113,6 +113,11 @@ public class BubbleTransitions {
return convert;
}
+ /** Starts a transition that converts a dragged bubble icon to a full screen task. */
+ public BubbleTransition startDraggedBubbleIconToFullscreen(Bubble bubble) {
+ return new DraggedBubbleIconToFullscreen(bubble);
+ }
+
/**
* Plucks the task-surface out of an ancestor view while making the view invisible. This helper
* attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent).
@@ -607,8 +612,7 @@ public class BubbleTransitions {
mTaskLeash = taskChg.getLeash();
mRootLeash = info.getRoot(0).getLeash();
- SurfaceControl dest =
- mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+ SurfaceControl dest = getExpandedView(mBubble).getViewRootImpl().getSurfaceControl();
final Runnable onPlucked = () -> {
// Need to remove the taskview AFTER applying the startTransaction because
// it isn't synchronized.
@@ -618,12 +622,12 @@ public class BubbleTransitions {
mBubbleData.setExpanded(false /* expanded */);
};
if (dest != null) {
- pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+ pluck(mTaskLeash, getExpandedView(mBubble), dest,
taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
- mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+ getCornerRadius(mBubble), startTransaction,
onPlucked);
- mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+ getExpandedView(mBubble).post(() -> mTransitions.dispatchTransition(
mTransition, info, startTransaction, finishTransaction, finishCallback,
null));
} else {
@@ -644,5 +648,130 @@ public class BubbleTransitions {
t.reparent(mTaskLeash, mRootLeash);
t.apply();
}
+
+ private View getExpandedView(@NonNull Bubble bubble) {
+ if (bubble.getBubbleBarExpandedView() != null) {
+ return bubble.getBubbleBarExpandedView();
+ }
+ return bubble.getExpandedView();
+ }
+
+ private float getCornerRadius(@NonNull Bubble bubble) {
+ if (bubble.getBubbleBarExpandedView() != null) {
+ return bubble.getBubbleBarExpandedView().getCornerRadius();
+ }
+ return bubble.getExpandedView().getCornerRadius();
+ }
+ }
+
+ /**
+ * A transition that converts a dragged bubble icon to a full screen window.
+ *
+ * <p>This transition assumes that the bubble is invisible so it is simply sent to front.
+ */
+ class DraggedBubbleIconToFullscreen implements Transitions.TransitionHandler, BubbleTransition {
+
+ IBinder mTransition;
+ final Bubble mBubble;
+
+ DraggedBubbleIconToFullscreen(Bubble bubble) {
+ mBubble = bubble;
+ bubble.setPreparingTransition(this);
+ WindowContainerToken token = bubble.getTaskView().getTaskInfo().getToken();
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setAlwaysOnTop(token, false);
+ wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(token, /* onTop= */ true);
+ wct.setHidden(token, false);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false);
+ mTaskViewTransitions.enqueueExternal(bubble.getTaskView().getController(), () -> {
+ mTransition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, this);
+ return mTransition;
+ });
+ }
+
+ @Override
+ public void skip() {
+ mBubble.setPreparingTransition(null);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mTransition != transition) {
+ return false;
+ }
+
+ final TaskViewTaskController taskViewTaskController =
+ mBubble.getTaskView().getController();
+ if (taskViewTaskController == null) {
+ mTaskViewTransitions.onExternalDone(transition);
+ finishCallback.onTransitionFinished(null);
+ return true;
+ }
+
+ TransitionInfo.Change change = findTransitionChange(info);
+ if (change == null) {
+ Slog.w(TAG, "Expected a TaskView transition to front but didn't find "
+ + "one, cleaning up the task view");
+ taskViewTaskController.setTaskNotFound();
+ mTaskViewTransitions.onExternalDone(transition);
+ finishCallback.onTransitionFinished(null);
+ return true;
+ }
+ mRepository.remove(taskViewTaskController);
+
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null);
+ taskViewTaskController.notifyTaskRemovalStarted(mBubble.getTaskView().getTaskInfo());
+ mTaskViewTransitions.onExternalDone(transition);
+ return true;
+ }
+
+ private TransitionInfo.Change findTransitionChange(TransitionInfo info) {
+ TransitionInfo.Change result = null;
+ WindowContainerToken token = mBubble.getTaskView().getTaskInfo().getToken();
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() == null) {
+ continue;
+ }
+ if (change.getMode() != TRANSIT_TO_FRONT) {
+ continue;
+ }
+ if (!token.equals(change.getTaskInfo().token)) {
+ continue;
+ }
+ result = change;
+ break;
+ }
+ return result;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) {
+ if (!aborted) {
+ return;
+ }
+ mTransition = null;
+ mTaskViewTransitions.onExternalDone(transition);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index b7761ec75782..69009fd1606a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -28,9 +28,9 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.app.animation.Interpolators;
-import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import java.util.ArrayList;
@@ -263,7 +263,7 @@ class BubbleBarMenuViewController {
}
));
- if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
menuActions.add(new BubbleBarMenuView.MenuAction(
Icon.createWithResource(resources,
R.drawable.desktop_mode_ic_handle_menu_fullscreen),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index c4696d5f44f4..a8e6b593f20d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -20,17 +20,14 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL;
import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.transition.LegacyTransitions;
import java.util.ArrayList;
@@ -87,25 +84,6 @@ public final class SyncTransactionQueue {
}
/**
- * Queues a legacy transition to be sent serially to WM
- */
- public void queue(LegacyTransitions.ILegacyTransition transition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- if (wct.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
- return;
- }
- SyncCallback cb = new SyncCallback(transition, type, wct);
- synchronized (mQueue) {
- if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
- mQueue.add(cb);
- if (mQueue.size() == 1) {
- cb.send();
- }
- }
- }
-
- /**
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
* Otherwise just returns without queueing.
* @return {@code true} if queued, {@code false} if not.
@@ -168,17 +146,9 @@ public final class SyncTransactionQueue {
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
- final LegacyTransitions.LegacyTransition mLegacyTransition;
SyncCallback(WindowContainerTransaction wct) {
mWCT = wct;
- mLegacyTransition = null;
- }
-
- SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- mWCT = wct;
- mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
}
// Must be sychronized on mQueue
@@ -194,12 +164,7 @@ public final class SyncTransactionQueue {
}
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
try {
- if (mLegacyTransition != null) {
- mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
- mLegacyTransition.getAdapter(), this, mWCT);
- } else {
- mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
- }
+ mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
} catch (RuntimeException e) {
Slog.e(TAG, "Send failed", e);
// Finish current sync callback immediately.
@@ -228,18 +193,10 @@ public final class SyncTransactionQueue {
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
- if (mLegacyTransition != null) {
- try {
- mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
- }
- } else {
- ProtoLog.v(WM_SHELL,
- "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
- t.apply();
- t.close();
- }
+ ProtoLog.v(WM_SHELL,
+ "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
+ t.apply();
+ t.close();
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
index 453ca167557a..1128fb2259b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
@@ -86,8 +86,7 @@ public class PipDesktopState {
return false;
}
final int displayId = mPipDisplayLayoutState.getDisplayId();
- return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId)
- || isDisplayInFreeform();
+ return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
index 4cbb78f2dae2..d36201a4ac9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java
@@ -52,24 +52,15 @@ public class PipDoubleTapHelper {
public static final int SIZE_SPEC_MAX = 1;
public static final int SIZE_SPEC_CUSTOM = 2;
- /**
- * Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from.
- *
- * <p>Each double tap toggles back and forth between {@code PipSizeSpec.CUSTOM} and
- * either {@code PipSizeSpec.MAX} or {@code PipSizeSpec.DEFAULT}. The choice between
- * the latter two sizes is determined based on the current state of the pip screen.</p>
- *
- * @param mPipBoundsState current state of the pip screen
- */
@PipSizeSpec
- private static int getMaxOrDefaultPipSizeSpec(@NonNull PipBoundsState mPipBoundsState) {
+ private static int getMaxOrDefaultPipSizeSpec(@NonNull PipBoundsState pipBoundsState) {
// determine the average pip screen width
- int averageWidth = (mPipBoundsState.getMaxSize().x
- + mPipBoundsState.getMinSize().x) / 2;
+ int averageWidth = (pipBoundsState.getMaxSize().x
+ + pipBoundsState.getMinSize().x) / 2;
// If pip screen width is above average, DEFAULT is the size spec we need to
// toggle to. Otherwise, we choose MAX.
- return (mPipBoundsState.getBounds().width() > averageWidth)
+ return (pipBoundsState.getBounds().width() > averageWidth)
? SIZE_SPEC_DEFAULT
: SIZE_SPEC_MAX;
}
@@ -77,35 +68,33 @@ public class PipDoubleTapHelper {
/**
* Determines the {@link PipSizeSpec} to toggle to on double tap.
*
- * @param mPipBoundsState current state of the pip screen
+ * @param pipBoundsState current state of the pip bounds
* @param userResizeBounds latest user resized bounds (by pinching in/out)
- * @return pip screen size to switch to
*/
@PipSizeSpec
- public static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState,
+ public static int nextSizeSpec(@NonNull PipBoundsState pipBoundsState,
@NonNull Rect userResizeBounds) {
- // is pip screen at its maximum
- boolean isScreenMax = mPipBoundsState.getBounds().width()
- == mPipBoundsState.getMaxSize().x;
-
- // is pip screen at its normal default size
- boolean isScreenDefault = (mPipBoundsState.getBounds().width()
- == mPipBoundsState.getNormalBounds().width())
- && (mPipBoundsState.getBounds().height()
- == mPipBoundsState.getNormalBounds().height());
+ boolean isScreenMax = pipBoundsState.getBounds().width() == pipBoundsState.getMaxSize().x
+ && pipBoundsState.getBounds().height() == pipBoundsState.getMaxSize().y;
+ boolean isScreenDefault = (pipBoundsState.getBounds().width()
+ == pipBoundsState.getNormalBounds().width())
+ && (pipBoundsState.getBounds().height()
+ == pipBoundsState.getNormalBounds().height());
// edge case 1
// if user hasn't resized screen yet, i.e. CUSTOM size does not exist yet
// or if user has resized exactly to DEFAULT, then we just want to maximize
if (isScreenDefault
- && userResizeBounds.width() == mPipBoundsState.getNormalBounds().width()) {
+ && userResizeBounds.width() == pipBoundsState.getNormalBounds().width()
+ && userResizeBounds.height() == pipBoundsState.getNormalBounds().height()) {
return SIZE_SPEC_MAX;
}
// edge case 2
- // if user has maximized, then we want to toggle to DEFAULT
+ // if user has resized to max, then we want to toggle to DEFAULT
if (isScreenMax
- && userResizeBounds.width() == mPipBoundsState.getMaxSize().x) {
+ && userResizeBounds.width() == pipBoundsState.getMaxSize().x
+ && userResizeBounds.height() == pipBoundsState.getMaxSize().y) {
return SIZE_SPEC_DEFAULT;
}
@@ -113,9 +102,6 @@ public class PipDoubleTapHelper {
if (isScreenDefault || isScreenMax) {
return SIZE_SPEC_CUSTOM;
}
-
- // if we are currently in user resized CUSTOM size state
- // then we toggle either to MAX or DEFAULT depending on the current pip screen state
- return getMaxOrDefaultPipSizeSpec(mPipBoundsState);
+ return getMaxOrDefaultPipSizeSpec(pipBoundsState);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 318cdeec5bc1..f62fd819319e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -95,6 +95,7 @@ import com.android.wm.shell.compatui.impl.DefaultComponentIdGenerator;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.common.DefaultHomePackageSupplier;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
@@ -260,8 +261,14 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
- return new DesktopModeCompatPolicy(context);
+ static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(
+ Context context,
+ ShellInit shellInit,
+ @ShellMainThread Handler mainHandler) {
+ final DesktopModeCompatPolicy policy = new DesktopModeCompatPolicy(context);
+ policy.setDefaultHomePackageSupplier(new DefaultHomePackageSupplier(
+ context, shellInit, mainHandler));
+ return policy;
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index bc2ed3f35b45..67a4d6cf89bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -98,6 +98,7 @@ import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeMoveToDisplayTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -780,6 +781,7 @@ public abstract class WMShellModule {
OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
DesksOrganizer desksOrganizer,
Optional<DesksTransitionObserver> desksTransitionObserver,
+ Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
UserProfileContexts userProfileContexts,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DragToDisplayTransitionHandler dragToDisplayTransitionHandler,
@@ -823,6 +825,7 @@ public abstract class WMShellModule {
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionObserver.get(),
+ desktopPipTransitionObserver,
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -1225,6 +1228,7 @@ public abstract class WMShellModule {
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
+ Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
Optional<BackAnimationController> backAnimationController,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
ShellInit shellInit) {
@@ -1237,6 +1241,7 @@ public abstract class WMShellModule {
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
+ desktopPipTransitionObserver,
backAnimationController.get(),
desktopWallpaperActivityTokenProvider,
shellInit)));
@@ -1258,6 +1263,19 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
+ Context context
+ ) {
+ if (DesktopModeStatus.canEnterDesktopMode(context)
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+ return Optional.of(
+ new DesktopPipTransitionObserver());
+ }
+ return Optional.empty();
+ }
+
+ @WMSingleton
+ @Provides
static Optional<DesktopMixedTransitionHandler> provideDesktopMixedTransitionHandler(
Context context,
Transitions transitions,
@@ -1474,6 +1492,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
InputManager inputManager,
+ DisplayController displayController,
@ShellMainThread Handler mainHandler
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -1488,6 +1507,7 @@ public abstract class WMShellModule {
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 904d86282c39..489e4f0aed01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -35,9 +35,11 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
/** Controls the display windowing mode in desktop mode */
@@ -49,6 +51,7 @@ class DesktopDisplayModeController(
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
private val inputManager: InputManager,
+ private val displayController: DisplayController,
@ShellMainThread private val mainHandler: Handler,
) {
@@ -111,37 +114,70 @@ class DesktopDisplayModeController(
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
+ // Do not directly use this method to check the state of desktop-first mode. Check the display
+ // windowing mode instead.
+ private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
+ if (isDefaultDisplayDesktopEligible()) {
+ if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+ return true
+ }
+ if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ if (isInClamshellMode()) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
@VisibleForTesting
fun getTargetWindowingModeForDefaultDisplay(): Int {
- if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+ if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
return WINDOWING_MODE_FREEFORM
}
- if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode()) {
- return WINDOWING_MODE_FREEFORM
- }
- return WINDOWING_MODE_FULLSCREEN
- }
- // If form factor-based desktop first switch is disabled, use the default display windowing
- // mode here to keep the freeform mode for some form factors (e.g., FEATURE_PC).
- return windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ WINDOWING_MODE_FULLSCREEN
+ } else {
+ // If form factor-based desktop first switch is disabled, use the default display
+ // windowing mode here to keep the freeform mode for some form factors (e.g.,
+ // FEATURE_PC).
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
}
- // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- private fun isExtendedDisplayEnabled() =
- 0 !=
+ private fun isExtendedDisplayEnabled(): Boolean {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
+ return rootTaskDisplayAreaOrganizer
+ .getDisplayIds()
+ .filter { it != DEFAULT_DISPLAY }
+ .any { displayId ->
+ displayController.getDisplay(displayId)?.let { display ->
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ } ?: false
+ }
+ }
+
+ return 0 !=
Settings.Global.getInt(
context.contentResolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
0,
)
+ }
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+ private fun isDefaultDisplayDesktopEligible(): Boolean {
+ val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+ "Display object of DEFAULT_DISPLAY must be non-null."
+ }
+ return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ }
+
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
new file mode 100644
index 000000000000..efd3866e1bc4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.os.IBinder
+import android.window.DesktopModeFlags
+import android.window.TransitionInfo
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+
+/**
+ * Observer of PiP in Desktop Mode transitions. At the moment, this is specifically tracking a PiP
+ * transition for a task that is entering PiP via the minimize button on the caption bar.
+ */
+class DesktopPipTransitionObserver {
+ private val pendingPipTransitions = mutableMapOf<IBinder, PendingPipTransition>()
+
+ /** Adds a pending PiP transition to be tracked. */
+ fun addPendingPipTransition(transition: PendingPipTransition) {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
+ pendingPipTransitions[transition.token] = transition
+ }
+
+ /**
+ * Called when any transition is ready, which may include transitions not tracked by this
+ * observer.
+ */
+ fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
+ val pipTransition = pendingPipTransitions.remove(transition) ?: return
+
+ logD("Desktop PiP transition ready: %s", transition)
+ for (change in info.changes) {
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) {
+ continue
+ }
+
+ if (
+ taskInfo.taskId == pipTransition.taskId &&
+ taskInfo.windowingMode == WINDOWING_MODE_PINNED
+ ) {
+ logD("Desktop PiP transition was successful")
+ pipTransition.onSuccess()
+ return
+ }
+ }
+ logD("Change with PiP task not found in Desktop PiP transition; likely failed")
+ }
+
+ /**
+ * Data tracked for a pending PiP transition.
+ *
+ * @property token the PiP transition that is started.
+ * @property taskId task id of the task entering PiP.
+ * @property onSuccess callback to be invoked if the PiP transition is successful.
+ */
+ data class PendingPipTransition(val token: IBinder, val taskId: Int, val onSuccess: () -> Unit)
+
+ private fun logD(msg: String, vararg arguments: Any?) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private companion object {
+ private const val TAG = "DesktopPipTransitionObserver"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 8636bc1f56c2..6cb26b54e802 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -68,7 +68,6 @@ class DesktopRepository(
* @property topTransparentFullscreenTaskId the task id of any current top transparent
* fullscreen task launched on top of the desk. Cleared when the transparent task is closed or
* sent to back. (top is at index 0).
- * @property pipTaskId the task id of PiP task entered while in Desktop Mode.
*/
private data class Desk(
val deskId: Int,
@@ -81,7 +80,6 @@ class DesktopRepository(
val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
var fullImmersiveTaskId: Int? = null,
var topTransparentFullscreenTaskId: Int? = null,
- var pipTaskId: Int? = null,
) {
fun deepCopy(): Desk =
Desk(
@@ -94,7 +92,6 @@ class DesktopRepository(
freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
fullImmersiveTaskId = fullImmersiveTaskId,
topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
- pipTaskId = pipTaskId,
)
// TODO: b/362720497 - remove when multi-desktops is enabled where instances aren't
@@ -107,7 +104,6 @@ class DesktopRepository(
freeformTasksInZOrder.clear()
fullImmersiveTaskId = null
topTransparentFullscreenTaskId = null
- pipTaskId = null
}
}
@@ -127,9 +123,6 @@ class DesktopRepository(
/* Tracks last bounds of task before toggled to immersive state. */
private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>()
- /* Callback for when a pending PiP transition has been aborted. */
- private var onPipAbortedCallback: ((Int, Int) -> Unit)? = null
-
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
@@ -339,7 +332,7 @@ class DesktopRepository(
val affectedDisplays = mutableSetOf<Int>()
desktopData
.desksSequence()
- .filter { desk -> desk.displayId != excludedDeskId }
+ .filter { desk -> desk.deskId != excludedDeskId }
.forEach { desk ->
val removed = removeActiveTaskFromDesk(desk.deskId, taskId, notifyListeners = false)
if (removed) {
@@ -611,57 +604,6 @@ class DesktopRepository(
}
/**
- * Set whether the given task is the Desktop-entered PiP task in this display's active desk.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun setTaskInPip(displayId: Int, taskId: Int, enterPip: Boolean) {
- val activeDesk =
- desktopData.getActiveDesk(displayId)
- ?: error("Expected active desk in display: $displayId")
- if (enterPip) {
- activeDesk.pipTaskId = taskId
- } else {
- activeDesk.pipTaskId =
- if (activeDesk.pipTaskId == taskId) null
- else {
- logW(
- "setTaskInPip: taskId=%d did not match saved taskId=%d",
- taskId,
- activeDesk.pipTaskId,
- )
- activeDesk.pipTaskId
- }
- }
- }
-
- /**
- * Returns whether the given task is the Desktop-entered PiP task in this display's active desk.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun isTaskMinimizedPipInDisplay(displayId: Int, taskId: Int): Boolean =
- desktopData.getActiveDesk(displayId)?.pipTaskId == taskId
-
- /**
- * Saves callback to handle a pending PiP transition being aborted.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun setOnPipAbortedCallback(callbackIfPipAborted: ((displayId: Int, pipTaskId: Int) -> Unit)?) {
- onPipAbortedCallback = callbackIfPipAborted
- }
-
- /**
- * Invokes callback to handle a pending PiP transition with the given task id being aborted.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun onPipAborted(displayId: Int, pipTaskId: Int) {
- onPipAbortedCallback?.invoke(displayId, pipTaskId)
- }
-
- /**
* Set whether the given task is the full-immersive task in this display's active desk.
*
* TODO: b/389960283 - consider forcing callers to use [setTaskInFullImmersiveStateInDesk] with
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 c28fdcb44f5b..cfc1541d6388 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
@@ -32,8 +32,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -45,6 +43,7 @@ import android.os.IBinder
import android.os.SystemProperties
import android.os.UserHandle
import android.util.Slog
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.MotionEvent
@@ -215,6 +214,7 @@ class DesktopTasksController(
private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
private val desksOrganizer: DesksOrganizer,
private val desksTransitionObserver: DesksTransitionObserver,
+ private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val userProfileContexts: UserProfileContexts,
private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
@@ -465,6 +465,10 @@ class DesktopTasksController(
/** Creates a new desk in the given display. */
fun createDesk(displayId: Int) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ logW("createDesk attempt with invalid displayId", displayId)
+ return
+ }
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desksOrganizer.createDesk(displayId) { deskId ->
taskRepository.addDesk(displayId = displayId, deskId = deskId)
@@ -653,6 +657,7 @@ class DesktopTasksController(
taskInfo: RunningTaskInfo,
dragToDesktopValueAnimator: MoveToDesktopAnimator,
taskSurface: SurfaceControl,
+ dragInterruptedCallback: Runnable,
) {
logV("startDragToDesktop taskId=%d", taskInfo.taskId)
val jankConfigBuilder =
@@ -668,6 +673,7 @@ class DesktopTasksController(
taskInfo,
dragToDesktopValueAnimator,
visualIndicator,
+ dragInterruptedCallback,
)
}
@@ -788,10 +794,31 @@ class DesktopTasksController(
fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val wct = WindowContainerTransaction()
-
+ val taskId = taskInfo.taskId
+ val displayId = taskInfo.displayId
+ val deskId =
+ taskRepository.getDeskIdForTask(taskInfo.taskId)
+ ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW("minimizeTask: desk not found for task: ${taskInfo.taskId}")
+ return
+ } else {
+ getDefaultDeskId(taskInfo.displayId)
+ }
+ val isLastTask =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ taskRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ displayId = displayId,
+ )
+ } else {
+ taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+ }
val isMinimizingToPip =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
- (taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false)
+ desktopPipTransitionObserver.isPresent &&
+ (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
+
// If task is going to PiP, start a PiP transition instead of a minimize transition
if (isMinimizingToPip) {
val requestInfo =
@@ -805,75 +832,63 @@ class DesktopTasksController(
)
val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- freeformTaskTransitionStarter.startPipTransition(wct)
- taskRepository.setTaskInPip(taskInfo.displayId, taskInfo.taskId, enterPip = true)
- taskRepository.setOnPipAbortedCallback { displayId, taskId ->
- minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!, minimizeReason)
- taskRepository.setTaskInPip(displayId, taskId, enterPip = false)
- }
- return
- }
-
- minimizeTaskInner(taskInfo, minimizeReason)
- }
- private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
- val taskId = taskInfo.taskId
- val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
- if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- logW("minimizeTaskInner: desk not found for task: ${taskInfo.taskId}")
- return
- }
- val displayId = taskInfo.displayId
- val wct = WindowContainerTransaction()
-
- snapEventHandler.removeTaskIfTiled(displayId, taskId)
- val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
- val desktopExitRunnable =
- performDesktopExitCleanUp(
- wct = wct,
- deskId = deskId,
- displayId = displayId,
- willExitDesktop = willExitDesktop,
- )
- // Notify immersive handler as it might need to exit immersive state.
- val exitResult =
- desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- taskInfo = taskInfo,
- reason = DesktopImmersiveController.ExitReason.MINIMIZED,
- )
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- desksOrganizer.minimizeTask(
- wct = wct,
- deskId = checkNotNull(deskId) { "Expected non-null deskId" },
- task = taskInfo,
+ desktopPipTransitionObserver.get().addPendingPipTransition(
+ DesktopPipTransitionObserver.PendingPipTransition(
+ token = freeformTaskTransitionStarter.startPipTransition(wct),
+ taskId = taskInfo.taskId,
+ onSuccess = {
+ onDesktopTaskEnteredPip(
+ taskId = taskId,
+ deskId = deskId,
+ displayId = taskInfo.displayId,
+ taskIsLastVisibleTaskBeforePip = isLastTask,
+ )
+ },
+ )
)
} else {
- wct.reorder(taskInfo.token, /* onTop= */ false)
- }
- val isLastTask =
+ snapEventHandler.removeTaskIfTiled(displayId, taskId)
+ val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
+ val desktopExitRunnable =
+ performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = willExitDesktop,
+ )
+ // Notify immersive handler as it might need to exit immersive state.
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ taskInfo = taskInfo,
+ reason = DesktopImmersiveController.ExitReason.MINIMIZED,
+ )
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- taskRepository.isOnlyVisibleNonClosingTaskInDesk(
- taskId = taskId,
+ desksOrganizer.minimizeTask(
+ wct = wct,
deskId = checkNotNull(deskId) { "Expected non-null deskId" },
- displayId = displayId,
+ task = taskInfo,
)
} else {
- taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+ wct.reorder(taskInfo.token, /* onTop= */ false)
}
- val transition =
- freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
- desktopTasksLimiter.ifPresent {
- it.addPendingMinimizeChange(
- transition = transition,
- displayId = displayId,
- taskId = taskId,
- minimizeReason = minimizeReason,
- )
+ val transition =
+ freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
+ desktopTasksLimiter.ifPresent {
+ it.addPendingMinimizeChange(
+ transition = transition,
+ displayId = displayId,
+ taskId = taskId,
+ minimizeReason = minimizeReason,
+ )
+ }
+ exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
+ desktopExitRunnable?.invoke(transition)
}
- exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
- desktopExitRunnable?.invoke(transition)
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+ doesAnyTaskRequireTaskbarRounding(displayId, taskId)
+ )
}
/** Move a task with given `taskId` to fullscreen */
@@ -1256,8 +1271,7 @@ class DesktopTasksController(
wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
}
- // TODO: b/391485148 - pass in the moving-to-desk |task| here to apply task-limit policy.
- val activationRunnable = addDeskActivationChanges(destinationDeskId, wct)
+ val activationRunnable = addDeskActivationChanges(destinationDeskId, wct, task)
if (Flags.enableDisplayFocusInShellTransitions()) {
// Bring the destination display to top with includingParents=true, so that the
@@ -1841,7 +1855,11 @@ class DesktopTasksController(
displayId: Int,
forceExitDesktop: Boolean,
): Boolean {
- if (forceExitDesktop && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ if (
+ forceExitDesktop &&
+ (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue)
+ ) {
// |forceExitDesktop| is true when the callers knows we'll exit desktop, such as when
// explicitly going fullscreen, so there's no point in checking the desktop state.
return true
@@ -1858,6 +1876,33 @@ class DesktopTasksController(
return true
}
+ /** Potentially perform Desktop cleanup after a task successfully enters PiP. */
+ @VisibleForTesting
+ fun onDesktopTaskEnteredPip(
+ taskId: Int,
+ deskId: Int,
+ displayId: Int,
+ taskIsLastVisibleTaskBeforePip: Boolean,
+ ) {
+ if (
+ !willExitDesktop(taskId, displayId, forceExitDesktop = taskIsLastVisibleTaskBeforePip)
+ ) {
+ return
+ }
+
+ val wct = WindowContainerTransaction()
+ val desktopExitRunnable =
+ performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = true,
+ )
+
+ val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+ desktopExitRunnable?.invoke(transition)
+ }
+
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
deskId: Int? = null,
@@ -2944,6 +2989,11 @@ class DesktopTasksController(
removeDesk(displayId = displayId, deskId = deskId)
}
+ /** Removes all the available desks on all displays. */
+ fun removeAllDesks() {
+ taskRepository.getAllDeskIds().forEach { deskId -> removeDesk(deskId) }
+ }
+
private fun removeDesk(displayId: Int, deskId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
@@ -3710,6 +3760,18 @@ class DesktopTasksController(
}
}
+ override fun removeDesk(deskId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDesk") { c ->
+ c.removeDesk(deskId)
+ }
+ }
+
+ override fun removeAllDesks() {
+ executeRemoteCallWithTaskPermission(controller, "removeAllDesks") { c ->
+ c.removeAllDesks()
+ }
+ }
+
override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
c.activateDesk(deskId, remoteTransition)
@@ -3773,8 +3835,8 @@ class DesktopTasksController(
}
}
- override fun removeDesktop(displayId: Int) {
- executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
+ override fun removeDefaultDeskInDisplay(displayId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDefaultDeskInDisplay") { c ->
c.removeDefaultDeskInDisplay(displayId)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 7dabeb7c9d15..df4d18f8c803 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -23,7 +23,6 @@ import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_OPEN
-import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
@@ -38,11 +37,12 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktop
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.TransitionUtil.isClosingMode
+import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
-import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
+import java.util.Optional
/**
* A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -55,6 +55,7 @@ class DesktopTasksTransitionObserver(
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
+ private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val backAnimationController: BackAnimationController,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
shellInit: ShellInit,
@@ -63,8 +64,6 @@ class DesktopTasksTransitionObserver(
data class CloseWallpaperTransition(val transition: IBinder, val displayId: Int)
private var transitionToCloseWallpaper: CloseWallpaperTransition? = null
- /* Pending PiP transition and its associated display id and task id. */
- private var pendingPipTransitionAndPipTask: Triple<IBinder, Int, Int>? = null
private var currentProfileId: Int
init {
@@ -98,33 +97,7 @@ class DesktopTasksTransitionObserver(
removeTaskIfNeeded(info)
}
removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
-
- val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
- info.changes.forEach { change ->
- change.taskInfo?.let { taskInfo ->
- if (
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
- desktopRepository.isTaskMinimizedPipInDisplay(
- taskInfo.displayId,
- taskInfo.taskId,
- )
- ) {
- when (info.type) {
- TRANSIT_PIP ->
- pendingPipTransitionAndPipTask =
- Triple(transition, taskInfo.displayId, taskInfo.taskId)
-
- TRANSIT_EXIT_PIP,
- TRANSIT_REMOVE_PIP ->
- desktopRepository.setTaskInPip(
- taskInfo.displayId,
- taskInfo.taskId,
- enterPip = false,
- )
- }
- }
- }
- }
+ desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
}
private fun removeTaskIfNeeded(info: TransitionInfo) {
@@ -299,18 +272,6 @@ class DesktopTasksTransitionObserver(
}
}
transitionToCloseWallpaper = null
- } else if (pendingPipTransitionAndPipTask?.first == transition) {
- val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
- if (aborted) {
- pendingPipTransitionAndPipTask?.let {
- desktopRepository.onPipAborted(
- /*displayId=*/ it.second,
- /* taskId=*/ it.third,
- )
- }
- }
- desktopRepository.setOnPipAbortedCallback(null)
- pendingPipTransitionAndPipTask = null
}
}
@@ -345,18 +306,29 @@ class DesktopTasksTransitionObserver(
}
private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
- info.changes.forEach { change ->
- change.taskInfo?.let { task ->
- val desktopRepository = desktopUserRepositories.getProfile(task.userId)
- val displayId = task.displayId
- // Clear `topTransparentFullscreenTask` information from repository if task
- // is closed or sent to back.
- if (
- TransitionUtil.isClosingMode(change.mode) &&
- task.taskId ==
- desktopRepository.getTopTransparentFullscreenTaskId(displayId)
- ) {
- desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ run forEachLoop@{
+ info.changes.forEach { change ->
+ change.taskInfo?.let { task ->
+ val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+ val displayId = task.displayId
+ val transparentTaskId =
+ desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+ if (transparentTaskId == null) return@forEachLoop
+ val changeMode = change.mode
+ val taskId = task.taskId
+ val isTopTransparentFullscreenTaskClosing =
+ taskId == transparentTaskId && isClosingMode(changeMode)
+ val isNonTopTransparentFullscreenTaskOpening =
+ taskId != transparentTaskId && isOpeningMode(changeMode)
+ // Clear `topTransparentFullscreenTask` information from repository if task
+ // is closed, sent to back or if a different task is opened, brought to front.
+ if (
+ isTopTransparentFullscreenTaskClosing ||
+ isNonTopTransparentFullscreenTaskOpening
+ ) {
+ desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ return@forEachLoop
+ }
}
}
}
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 24b2e4879546..c6f74728fd81 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
@@ -2,6 +2,7 @@ package com.android.wm.shell.desktopmode
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
@@ -23,6 +24,7 @@ import android.os.IBinder
import android.os.SystemClock
import android.os.SystemProperties
import android.os.UserHandle
+import android.view.Choreographer
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CLOSE
@@ -48,6 +50,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKT
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.shared.animation.PhysicsAnimator
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
@@ -122,6 +125,7 @@ sealed class DragToDesktopTransitionHandler(
taskInfo: RunningTaskInfo,
dragToDesktopAnimator: MoveToDesktopAnimator,
visualIndicator: DesktopModeVisualIndicator?,
+ dragCancelCallback: Runnable,
) {
if (inProgress) {
logV("Drag to desktop transition already in progress.")
@@ -168,6 +172,7 @@ sealed class DragToDesktopTransitionHandler(
startTransitionToken = startTransitionToken,
otherSplitTask = otherTask,
visualIndicator = visualIndicator,
+ dragCancelCallback = dragCancelCallback,
)
} else {
TransitionState.FromFullscreen(
@@ -175,6 +180,7 @@ sealed class DragToDesktopTransitionHandler(
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
visualIndicator = visualIndicator,
+ dragCancelCallback = dragCancelCallback,
)
}
}
@@ -203,8 +209,9 @@ sealed class DragToDesktopTransitionHandler(
}
if (state.startInterrupted) {
logV("finishDragToDesktop: start was interrupted, returning")
- // We should only have interrupted the start transition after receiving a cancel/end
- // request, let that existing request play out and just return here.
+ // If start was interrupted we've either already requested a cancel/end transition - so
+ // we should let that request play out, or we're cancelling the drag-to-desktop
+ // transition altogether, so just return here.
return null
}
state.endTransitionToken =
@@ -221,6 +228,7 @@ sealed class DragToDesktopTransitionHandler(
*/
fun cancelDragToDesktopTransition(cancelState: CancelState) {
if (!inProgress) {
+ logV("cancelDragToDesktop: not in progress, returning")
// Don't attempt to cancel a drag to desktop transition since there is no transition in
// progress which means that the drag to desktop transition was never successfully
// started.
@@ -228,14 +236,17 @@ sealed class DragToDesktopTransitionHandler(
}
val state = requireTransitionState()
if (state.startAborted) {
+ logV("cancelDragToDesktop: start was aborted, clearing state")
// Don't attempt to cancel the drag-to-desktop since the start transition didn't
// succeed as expected. Just reset the state as if nothing happened.
clearState()
return
}
if (state.startInterrupted) {
- // We should only have interrupted the start transition after receiving a cancel/end
- // request, let that existing request play out and just return here.
+ logV("cancelDragToDesktop: start was interrupted, returning")
+ // If start was interrupted we've either already requested a cancel/end transition - so
+ // we should let that request play out, or we're cancelling the drag-to-desktop
+ // transition altogether, so just return here.
return
}
state.cancelState = cancelState
@@ -706,11 +717,7 @@ sealed class DragToDesktopTransitionHandler(
// end-transition, or if the end-transition is running on its own, then just wait until that
// finishes instead. If we've merged the cancel-transition we've finished the
// start-transition and won't reach this code.
- if (
- mergeTarget == state.startTransitionToken &&
- isCancelOrEndTransitionRequested(state) &&
- !state.mergedEndTransition
- ) {
+ if (mergeTarget == state.startTransitionToken && !state.mergedEndTransition) {
interruptStartTransition(state)
}
}
@@ -722,9 +729,23 @@ sealed class DragToDesktopTransitionHandler(
if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue) {
return
}
- logV("interruptStartTransition")
- state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
- state.dragAnimator.cancelAnimator()
+ if (isCancelOrEndTransitionRequested(state)) {
+ logV("interruptStartTransition, bookend requested -> finish start transition")
+ // Finish the start-drag transition, we will finish the overall transition properly when
+ // receiving #startAnimation for Cancel/End.
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+ state.dragAnimator.cancelAnimator()
+ } else {
+ logV("interruptStartTransition, bookend not requested -> animate to Home")
+ // Animate to Home, and then finish the start-drag transition. Since there is no other
+ // (end/cancel) transition requested that will be the end of the overall transition.
+ state.dragAnimator.cancelAnimator()
+ state.dragCancelCallback?.run()
+ createInterruptToHomeAnimator(transactionSupplier.get(), state) {
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+ clearState()
+ }
+ }
state.activeCancelAnimation?.removeAllListeners()
state.activeCancelAnimation?.cancel()
state.activeCancelAnimation = null
@@ -738,6 +759,46 @@ sealed class DragToDesktopTransitionHandler(
.onActionCancel(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG)
}
+ private fun createInterruptToHomeAnimator(
+ transaction: Transaction,
+ state: TransitionState,
+ endCallback: Runnable,
+ ) {
+ val homeLeash = state.homeChange?.leash ?: error("Expected home leash to be non-null")
+ val draggedTaskLeash =
+ state.draggedTaskChange?.leash ?: error("Expected dragged leash to be non-null")
+ val homeAnimator = createInterruptAlphaAnimator(transaction, homeLeash, toShow = true)
+ val draggedTaskAnimator =
+ createInterruptAlphaAnimator(transaction, draggedTaskLeash, toShow = false)
+ val animatorSet = AnimatorSet()
+ animatorSet.playTogether(homeAnimator, draggedTaskAnimator)
+ animatorSet.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ endCallback.run()
+ }
+ }
+ )
+ animatorSet.start()
+ }
+
+ private fun createInterruptAlphaAnimator(
+ transaction: Transaction,
+ leash: SurfaceControl,
+ toShow: Boolean,
+ ) =
+ ValueAnimator.ofFloat(if (toShow) 0f else 1f, if (toShow) 1f else 0f).apply {
+ transaction.show(leash)
+ duration = DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { animation ->
+ transaction
+ .setAlpha(leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
+ }
+ }
+
protected open fun setupEndDragToDesktop(
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
@@ -1060,6 +1121,7 @@ sealed class DragToDesktopTransitionHandler(
abstract var endTransitionToken: IBinder?
abstract var mergedEndTransition: Boolean
abstract var activeCancelAnimation: Animator?
+ abstract var dragCancelCallback: Runnable?
data class FromFullscreen(
override val draggedTaskId: Int,
@@ -1079,6 +1141,7 @@ sealed class DragToDesktopTransitionHandler(
override var endTransitionToken: IBinder? = null,
override var mergedEndTransition: Boolean = false,
override var activeCancelAnimation: Animator? = null,
+ override var dragCancelCallback: Runnable? = null,
var otherRootChanges: MutableList<Change> = mutableListOf(),
) : TransitionState()
@@ -1100,6 +1163,7 @@ sealed class DragToDesktopTransitionHandler(
override var endTransitionToken: IBinder? = null,
override var mergedEndTransition: Boolean = false,
override var activeCancelAnimation: Animator? = null,
+ override var dragCancelCallback: Runnable? = null,
var splitRootChange: Change? = null,
var otherSplitTask: Int,
) : TransitionState()
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 44f7e16e98c3..5f7fbd9843d4 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
@@ -35,6 +35,12 @@ interface IDesktopMode {
/** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
+ /** Removes the desk with the given `deskId`. */
+ oneway void removeDesk(int deskId);
+
+ /** Removes all the available desks on all displays. */
+ oneway void removeAllDesks();
+
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -64,8 +70,11 @@ interface IDesktopMode {
in @nullable RemoteTransition remoteTransition,
in @nullable IMoveToDesktopCallback callback);
- /** Remove desktop on the given display */
- oneway void removeDesktop(int displayId);
+ /**
+ * Removes the default desktop on the given display.
+ * @deprecated with multi-desks, we should use `removeDesk()`.
+ */
+ oneway void removeDefaultDeskInDisplay(int displayId);
/** Move a task with given `taskId` to external display */
void moveToExternalDisplay(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
new file mode 100644
index 000000000000..8ce624e103ef
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import java.util.function.Supplier
+
+/**
+ * This supplies the package name of default home in an efficient way. The query to package manager
+ * only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ */
+class DefaultHomePackageSupplier(
+ private val context: Context,
+ shellInit: ShellInit,
+ @ShellMainThread private val mainHandler: Handler,
+) : BroadcastReceiver(), Supplier<String?> {
+
+ private var defaultHomePackage: String? = null
+
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ private fun onInit() {
+ context.registerReceiver(
+ this,
+ IntentFilter(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED),
+ null /* broadcastPermission */,
+ mainHandler,
+ )
+ }
+
+ private fun updateDefaultHomePackage(): String? {
+ defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ return defaultHomePackage
+ }
+
+ override fun onReceive(contxt: Context?, intent: Intent?) {
+ updateDefaultHomePackage()
+ }
+
+ override fun get(): String? {
+ return defaultHomePackage ?: updateDefaultHomePackage()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f89ba0a168d1..0bf2ea61b0a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -174,7 +174,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
SurfaceControl.Transaction finishT) {
mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToFrontTransitionReady(
@@ -184,7 +184,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToBackTransitionReady(
@@ -194,7 +194,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
@Override
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 f81f330e50c4..a02a51f92b1b 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
@@ -180,10 +180,17 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(pipTaskToken, toBounds);
if (configAtEnd) {
wct.deferConfigToTransitionEnd(pipTaskToken);
+
+ if (mPipBoundsState.getBounds().width() == toBounds.width()
+ && mPipBoundsState.getBounds().height() == toBounds.height()) {
+ // TODO (b/393159816): Config-at-End causes a flicker without size change.
+ // If PiP size isn't changing enforce a minimal one-pixel change as a workaround.
+ --toBounds.bottom;
+ }
}
+ wct.setBounds(pipTaskToken, toBounds);
mPipTransitionController.startResizeTransition(wct, duration);
}
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 6fdfecaf15d5..d1bc450c3c4d 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
@@ -934,6 +934,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
}
// the size to toggle to after a double tap
+ mPipBoundsState.setNormalBounds(getAdjustedNormalBounds());
int nextSize = PipDoubleTapHelper
.nextSizeSpec(mPipBoundsState, getUserResizeBounds());
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 75c09829e551..a7cba76ea91f 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
@@ -123,6 +123,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -675,7 +676,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!enteredSplitSelect) {
return null;
}
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
+ && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mTaskOrganizer.applyTransaction(wct);
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index 34d1011bac0e..f652e3149d9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -85,8 +85,8 @@ class WindowlessSnapshotWindowCreator {
final ActivityManager.TaskDescription taskDescription =
SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
- final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface,
- taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
+ final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, rootSurface,
+ wlw.mChildSurface, taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
runningTaskInfo.topActivityType, removeExecutor,
taskId, mStartingWindowRecordManager);
mStartingWindowRecordManager.addRecord(taskId, record);
@@ -96,14 +96,16 @@ class WindowlessSnapshotWindowCreator {
private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
private SurfaceControlViewHost mViewHost;
private SurfaceControl mChildSurface;
+ private SurfaceControl mRootSurface;
private final boolean mHasImeSurface;
- SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface,
- int bgColor, boolean hasImeSurface, int activityType,
+ SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl rootSurface,
+ SurfaceControl childSurface, int bgColor, boolean hasImeSurface, int activityType,
ShellExecutor removeExecutor, int id,
StartingSurfaceDrawer.StartingWindowRecordManager recordManager) {
super(activityType, removeExecutor, id, recordManager);
mViewHost = viewHost;
+ mRootSurface = rootSurface;
mChildSurface = childSurface;
mBGColor = bgColor;
mHasImeSurface = hasImeSurface;
@@ -145,6 +147,10 @@ class WindowlessSnapshotWindowCreator {
mTransactionPool.release(t);
mChildSurface = null;
}
+ if (mRootSurface != null && mRootSurface.isValid()) {
+ mRootSurface.release();
+ }
+ mRootSurface = null;
if (mViewHost != null) {
mViewHost.release();
mViewHost = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index f5aaaad93229..ce98b03b77a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -137,8 +137,7 @@ public class DefaultSurfaceAnimator {
if (mClipRect != null) {
boolean needCrop = false;
mAnimClipRect.set(mClipRect);
- if (transformation.hasClipRect()
- && com.android.window.flags.Flags.respectAnimationClip()) {
+ if (transformation.hasClipRect()) {
mAnimClipRect.intersectUnchecked(transformation.getClipRect());
needCrop = true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 938885cc1684..23dfb41d52c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DesktopModeFlags.ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
@@ -50,6 +51,7 @@ public class HomeTransitionObserver implements TransitionObserver,
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private IBinder mPendingStartDragTransition;
private Boolean mPendingHomeVisibilityUpdate;
public HomeTransitionObserver(@NonNull Context context,
@@ -63,31 +65,42 @@ public class HomeTransitionObserver implements TransitionObserver,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
- if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
- handleTransitionReadyWithBubbleAnything(info);
- } else {
- handleTransitionReady(info);
+ Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info);
+
+ if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+ // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+ // Store the value and apply with a next transition or when cancelling the
+ // desktop-drag transition.
+ storePendingHomeVisibilityUpdate(transition, homeVisibilityUpdate);
+ return;
+ }
+
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && info.getType() == TRANSIT_CONVERT_TO_BUBBLE
+ && homeVisibilityUpdate == null) {
+ // We are converting to bubble and we did not get a change to home visibility in this
+ // transition. Apply the value from start of drag.
+ homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+ }
+
+ if (homeVisibilityUpdate != null) {
+ mPendingHomeVisibilityUpdate = null;
+ mPendingStartDragTransition = null;
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
}
}
- private void handleTransitionReady(@NonNull TransitionInfo info) {
- for (TransitionInfo.Change change : info.getChanges()) {
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null
- || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
- || taskInfo.displayId != DEFAULT_DISPLAY
- || taskInfo.taskId == -1
- || !taskInfo.isRunning) {
- continue;
- }
- Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
- if (homeVisibilityUpdate != null) {
- notifyHomeVisibilityChanged(homeVisibilityUpdate);
- }
+ private void storePendingHomeVisibilityUpdate(
+ IBinder transition, Boolean homeVisibilityUpdate) {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && !ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+ return;
}
+ mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+ mPendingStartDragTransition = transition;
}
- private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+ private Boolean getHomeVisibilityUpdate(TransitionInfo info) {
Boolean homeVisibilityUpdate = null;
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -97,29 +110,12 @@ public class HomeTransitionObserver implements TransitionObserver,
|| !taskInfo.isRunning) {
continue;
}
-
Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
if (update != null) {
homeVisibilityUpdate = update;
}
}
-
- if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
- // Do not apply at the start of desktop drag as that updates launcher UI visibility.
- // Store the value and apply with a next transition if needed.
- mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
- return;
- }
-
- if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
- // We are converting to bubble and we did not get a change to home visibility in this
- // transition. Apply the value from start of drag.
- homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
- }
- if (homeVisibilityUpdate != null) {
- mPendingHomeVisibilityUpdate = null;
- notifyHomeVisibilityChanged(homeVisibilityUpdate);
- }
+ return homeVisibilityUpdate;
}
private Boolean getHomeVisibilityUpdate(TransitionInfo info,
@@ -146,7 +142,24 @@ public class HomeTransitionObserver implements TransitionObserver,
@Override
public void onTransitionFinished(@NonNull IBinder transition,
- boolean aborted) {}
+ boolean aborted) {
+ if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+ return;
+ }
+ // Handle the case where the DragToDesktop START transition is interrupted and we never
+ // receive a CANCEL/END transition.
+ if (mPendingStartDragTransition == null
+ || mPendingStartDragTransition != transition) {
+ return;
+ }
+ mPendingStartDragTransition = null;
+ if (aborted) return;
+
+ if (mPendingHomeVisibilityUpdate != null) {
+ notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate);
+ mPendingHomeVisibilityUpdate = null;
+ }
+ }
/**
* Sets the home transition listener that receives any transitions resulting in a change of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
deleted file mode 100644
index 978b8da2eb6d..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.transition;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerTransactionCallback;
-
-import com.android.internal.protolog.ProtoLog;
-
-/**
- * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
- * synctransaction tools.
- */
-public class LegacyTransitions {
-
- /**
- * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
- * into one callback.
- */
- public interface ILegacyTransition {
- /**
- * Called when both the associated sync transaction finishes and the remote animation is
- * ready.
- */
- void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
- }
-
- /**
- * Makes sure that a remote animation and corresponding sync callback are called together
- * such that the sync callback is called first. This assumes that both the callback receiver
- * and the remoteanimation are in the same process so that order is preserved on both ends.
- */
- public static class LegacyTransition {
- private final ILegacyTransition mLegacyTransition;
- private int mSyncId = -1;
- private SurfaceControl.Transaction mTransaction;
- private int mTransit;
- private RemoteAnimationTarget[] mApps;
- private RemoteAnimationTarget[] mWallpapers;
- private RemoteAnimationTarget[] mNonApps;
- private IRemoteAnimationFinishedCallback mFinishCallback = null;
- private boolean mCancelled = false;
- private final SyncCallback mSyncCallback = new SyncCallback();
- private final RemoteAnimationAdapter mAdapter =
- new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
-
- public LegacyTransition(@WindowManager.TransitionType int type,
- @NonNull ILegacyTransition legacyTransition) {
- mLegacyTransition = legacyTransition;
- mTransit = type;
- }
-
- public @WindowManager.TransitionType int getType() {
- return mTransit;
- }
-
- public IWindowContainerTransactionCallback getSyncCallback() {
- return mSyncCallback;
- }
-
- public RemoteAnimationAdapter getAdapter() {
- return mAdapter;
- }
-
- private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t)
- throws RemoteException {
- ProtoLog.v(WM_SHELL_TRANSITIONS,
- "LegacyTransitions.onTransactionReady(): syncId=%d", id);
- mSyncId = id;
- mTransaction = t;
- checkApply(true /* log */);
- }
- }
-
- private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- mTransit = transit;
- mApps = apps;
- mWallpapers = wallpapers;
- mNonApps = nonApps;
- mFinishCallback = finishedCallback;
- checkApply(false /* log */);
- }
-
- @Override
- public void onAnimationCancelled() throws RemoteException {
- mCancelled = true;
- mApps = mWallpapers = mNonApps = null;
- checkApply(false /* log */);
- }
- }
-
-
- private void checkApply(boolean log) throws RemoteException {
- if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) {
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tSkipping hasFinishedCb=%b canceled=%b",
- mFinishCallback != null, mCancelled);
- }
- return;
- }
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tapply");
- }
- mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
- mNonApps, mFinishCallback, mTransaction);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 7871179a50de..42321e56e72b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -49,6 +49,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.window.DisplayAreaInfo;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -233,7 +234,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 2d7fec3a5302..5e8c1fe2aa8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -445,4 +445,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
return R.dimen.freeform_decor_caption_height;
}
+
+ @Override
+ int getCaptionViewId() {
+ return R.id.caption;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 2b2cdf84005c..4511fbe10764 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,6 +31,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -159,7 +160,8 @@ public abstract class CarWindowDecorViewModel
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index 88f64bca280d..3182745d813e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -108,6 +108,11 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
return new Rect();
}
+ @Override
+ int getCaptionViewId() {
+ return R.id.caption;
+ }
+
private void updateRelayoutParams(
RelayoutParams relayoutParams,
ActivityManager.RunningTaskInfo taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index a1d2774ee428..f2ff39627362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -27,6 +27,7 @@ import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -79,6 +80,7 @@ import android.view.ViewConfiguration;
import android.view.ViewRootImpl;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -150,18 +152,19 @@ import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
-import org.jetbrains.annotations.NotNull;
-
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -206,6 +209,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final AppToWebEducationController mAppToWebEducationController;
private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -331,6 +335,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
new AppHeaderViewHolder.Factory(),
+ new AppHandleViewHolder.Factory(),
rootTaskDisplayAreaOrganizer,
new SparseArray<>(),
interactionJankMonitor,
@@ -380,6 +385,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
InteractionJankMonitor interactionJankMonitor,
@@ -422,6 +428,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mInputManager = mContext.getSystemService(InputManager.class);
@@ -486,7 +493,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()
+ || DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mRecentsTransitionHandler.addTransitionStateListener(
new DesktopModeRecentsTransitionStateListener());
}
@@ -587,7 +595,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
@@ -601,8 +610,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
+ /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
}
}
@@ -617,7 +626,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
}
@Override
@@ -817,9 +826,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return;
}
decoration.closeHandleMenu();
- // When the app enters split-select, the handle will no longer be visible, meaning
- // we shouldn't receive input for it any longer.
- decoration.disposeStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
@@ -979,6 +985,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private boolean mIsCustomHeaderGesture;
private boolean mIsResizeGesture;
private boolean mIsDragging;
+ private boolean mDragInterrupted;
private boolean mLongClickDisabled;
private int mDragPointerId = -1;
private MotionEvent mMotionEvent;
@@ -1216,7 +1223,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
View v, MotionEvent e) {
final int id = v.getId();
if (id == R.id.caption_handle) {
- handleCaptionThroughStatusBar(e, decoration);
+ handleCaptionThroughStatusBar(e, decoration,
+ /* interruptDragCallback= */
+ () -> {
+ mDragInterrupted = true;
+ setIsDragging(decoration, /* isDragging= */ false);
+ });
final boolean wasDragging = mIsDragging;
updateDragStatus(decoration, e);
final boolean upOrCancel = e.getActionMasked() == ACTION_UP
@@ -1333,11 +1345,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ mDragInterrupted = false;
setIsDragging(decor, false /* isDragging */);
break;
}
case MotionEvent.ACTION_MOVE: {
- setIsDragging(decor, true /* isDragging */);
+ if (!mDragInterrupted) {
+ setIsDragging(decor, true /* isDragging */);
+ }
break;
}
}
@@ -1458,7 +1473,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
if (!mInImmersiveMode && (relevantDecor == null
|| relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
|| mTransitionDragActive)) {
- handleCaptionThroughStatusBar(ev, relevantDecor);
+ handleCaptionThroughStatusBar(ev, relevantDecor,
+ /* interruptDragCallback= */ () -> {});
}
}
handleEventOutsideCaption(ev, relevantDecor);
@@ -1498,7 +1514,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
* Turn on desktop mode if handle is dragged below status bar.
*/
private void handleCaptionThroughStatusBar(MotionEvent ev,
- DesktopModeWindowDecoration relevantDecor) {
+ DesktopModeWindowDecoration relevantDecor, Runnable interruptDragCallback) {
if (relevantDecor == null) {
if (ev.getActionMasked() == ACTION_UP) {
mMoveToDesktopAnimator = null;
@@ -1599,7 +1615,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mContext, mDragToDesktopAnimationStartBounds,
relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
+ mMoveToDesktopAnimator, relevantDecor.mTaskSurface,
+ /* dragInterruptedCallback= */ () -> {
+ // Don't call into DesktopTasksController to cancel the
+ // transition here - the transition handler already handles
+ // that (including removing the visual indicator).
+ mTransitionDragActive = false;
+ mMoveToDesktopAnimator = null;
+ relevantDecor.handleDragInterrupted();
+ interruptDragCallback.run();
+ });
}
}
if (mMoveToDesktopAnimator != null) {
@@ -1761,6 +1786,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mMainChoreographer,
mSyncQueue,
mAppHeaderViewHolderFactory,
+ mAppHandleViewHolderFactory,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
mAssistContentRequester,
@@ -1852,12 +1878,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
incrementEventReceiverTasks(taskInfo.displayId);
}
}
+ @Nullable
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 1b5fdbb973d5..bcf9396ff0c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -190,6 +190,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private ExclusionRegionListener mExclusionRegionListener;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final MaximizeMenuFactory mMaximizeMenuFactory;
private final HandleMenuFactory mHandleMenuFactory;
@@ -212,6 +213,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
+ private boolean mIsMovingToBack;
public DesktopModeWindowDecoration(
Context context,
@@ -231,6 +233,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -242,10 +245,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
- appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
- assistContentRequester, SurfaceControl.Builder::new,
- SurfaceControl.Transaction::new, WindowContainerTransaction::new,
- SurfaceControl::new, new WindowManagerWrapper(
+ appHeaderViewHolderFactory, appHandleViewHolderFactory,
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
new SurfaceControlViewHostFactory() {},
windowDecorViewHostSupplier,
@@ -273,6 +276,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -302,6 +306,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mAssistContentRequester = assistContentRequester;
@@ -462,7 +467,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion);
+ hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
if (!applyTransactionOnDraw) {
t.apply();
}
@@ -489,7 +494,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
+ boolean isMovingToBack) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -512,12 +518,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
.isTaskInFullImmersiveState(taskInfo.taskId);
+ mIsMovingToBack = isMovingToBack;
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mIsDragging, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning,
- mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
+ displayExclusionRegion,
+ /* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
+ && DesktopModeFlags
+ .ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
+ mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
+ mIsRecentsTransitionRunning, mIsMovingToBack);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -542,7 +553,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return;
}
- if (oldRootView != mResult.mRootView) {
+ if (DesktopModeFlags.SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX.isTrue()
+ ? (oldRootView != mResult.mRootView && taskInfo.isVisibleRequested)
+ : oldRootView != mResult.mRootView) {
disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
// Load these only when first creating the view.
@@ -558,29 +571,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
});
}
- final Point position = new Point();
- if (isAppHandle(mWindowDecorViewHolder)) {
- position.set(determineHandlePosition());
- }
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyCaptionStateChanged();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
if (isAppHandle(mWindowDecorViewHolder)) {
- mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
- mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
- isCaptionVisible()
- ));
+ updateAppHandleViewHolder();
} else {
- mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
- mTaskInfo,
- DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
- inFullImmersive,
- hasGlobalFocus,
- /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
- /* animatingTaskResizeOrReposition= */ false)
- ));
+ updateAppHeaderViewHolder(inFullImmersive, hasGlobalFocus);
}
Trace.endSection();
@@ -857,9 +856,31 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
}
+ /** Update the view holder for app handle. */
+ private void updateAppHandleViewHolder() {
+ if (!isAppHandle(mWindowDecorViewHolder)) return;
+ asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
+ mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
+ mResult.mCaptionHeight, isCaptionVisible()
+ ));
+ }
+
+ /** Update the view holder for app header. */
+ private void updateAppHeaderViewHolder(boolean inFullImmersive, boolean hasGlobalFocus) {
+ if (!isAppHeader(mWindowDecorViewHolder)) return;
+ asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+ mTaskInfo,
+ DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
+ inFullImmersive,
+ hasGlobalFocus,
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+ /* animatingTaskResizeOrReposition= */ false)
+ ));
+ }
+
private WindowDecorationViewHolder createViewHolder() {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
- return new AppHandleViewHolder(
+ return mAppHandleViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
@@ -886,6 +907,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return viewHolder instanceof AppHandleViewHolder;
}
+ private boolean isAppHeader(WindowDecorationViewHolder viewHolder) {
+ return viewHolder instanceof AppHeaderViewHolder;
+ }
+
@Nullable
private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
if (viewHolder instanceof AppHandleViewHolder) {
@@ -918,7 +943,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion,
boolean shouldIgnoreCornerRadius,
- boolean shouldExcludeCaptionFromAppBounds) {
+ boolean shouldExcludeCaptionFromAppBounds,
+ boolean isRecentsTransitionRunning,
+ boolean isMovingToBack) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -935,11 +962,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// the first frame.
relayoutParams.mAsyncViewHost = isAppHandle;
- final boolean showCaption;
- if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+ boolean showCaption;
+ // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
+ // show caption (this includes split select transition).
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isMovingToBack && !isDragging) {
+ showCaption = false;
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
// If the task is being dragged, the caption should not be hidden so that it continues
// receiving input
showCaption = true;
+ } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isRecentsTransitionRunning) {
+ // Caption should not be visible in recents.
+ showCaption = false;
} else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
if (inFullImmersiveMode) {
showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1683,6 +1719,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
}
+ /**
+ * Indicates that an app handle drag has been interrupted, this can happen e.g. if we receive an
+ * unknown transition during the drag-to-desktop transition.
+ */
+ void handleDragInterrupted() {
+ if (mResult.mRootView == null) return;
+ final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
+ handle.setHovered(false);
+ handle.setPressed(false);
+ }
+
private boolean pointInView(View v, float x, float y) {
return v != null && v.getLeft() <= x && v.getRight() >= x
&& v.getTop() <= y && v.getBottom() >= y;
@@ -1776,6 +1823,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
}
+ @Override
+ int getCaptionViewId() {
+ return R.id.desktop_mode_caption;
+ }
+
void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return;
final boolean inFullImmersive =
@@ -1795,9 +1847,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* <p> When a Recents transition is active we allow that transition to take ownership of the
* corner radius of its task surfaces, so each window decoration should stop updating the corner
* radius of its task surface during that time.
+ *
+ * We should not allow input to reach the input layer during a Recents transition, so
+ * update the handle view holder accordingly if transition status changes.
*/
void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
+ mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+ // We don't relayout decor on recents transition, so we need to call it directly.
+ relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
+ }
+ }
}
/**
@@ -1862,6 +1923,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -1889,6 +1951,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
choreographer,
syncQueue,
appHeaderViewHolderFactory,
+ appHandleViewHolderFactory,
rootTaskDisplayAreaOrganizer,
genericLinksParser,
assistContentRequester,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 7a4a834e9dc2..0b86d1dbbc58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -23,12 +23,12 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
@@ -127,7 +127,9 @@ class DragResizeInputListener implements AutoCloseable {
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
DisplayController displayController,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ InputChannel inputChannel,
+ InputChannel sinkInputChannel) {
mContext = context;
mWindowSession = windowSession;
mBgExecutor = bgExecutor;
@@ -136,7 +138,11 @@ class DragResizeInputListener implements AutoCloseable {
mHandler = handler;
mChoreographer = choreographer;
mDisplayId = displayId;
- mDecorationSurface = decorationSurface;
+ // Creates a new SurfaceControl pointing the same underlying surface with decorationSurface
+ // to ensure that mDecorationSurface will not be released while it's used on the background
+ // thread. Note that the empty name will be overridden by the next copyFrom call.
+ mDecorationSurface = surfaceControlBuilderSupplier.get().setName("").build();
+ mDecorationSurface.copyFrom(decorationSurface, "DragResizeInputListener");
mDragPositioningCallback = callback;
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
@@ -154,9 +160,13 @@ class DragResizeInputListener implements AutoCloseable {
final InputSetUpResult result = setUpInputChannels(mDisplayId, mWindowSession,
mDecorationSurface, mClientToken, mSinkClientToken,
mSurfaceControlBuilderSupplier,
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, inputChannel, sinkInputChannel);
mainExecutor.execute(() -> {
if (mClosed) {
+ result.mInputChannel.dispose();
+ result.mSinkInputChannel.dispose();
+ mSurfaceControlTransactionSupplier.get().remove(
+ result.mInputSinkSurface).apply();
return;
}
mInputSinkSurface = result.mInputSinkSurface;
@@ -208,7 +218,7 @@ class DragResizeInputListener implements AutoCloseable {
new DefaultTaskResizeInputEventReceiverFactory(), taskInfo,
handler, choreographer, displayId, decorationSurface, callback,
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
- displayController, desktopModeEventLogger);
+ displayController, desktopModeEventLogger, new InputChannel(), new InputChannel());
}
DragResizeInputListener(
@@ -251,11 +261,11 @@ class DragResizeInputListener implements AutoCloseable {
@NonNull IBinder clientToken,
@NonNull IBinder sinkClientToken,
@NonNull Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ @NonNull InputChannel inputChannel,
+ @NonNull InputChannel sinkInputChannel) {
Trace.beginSection("DragResizeInputListener#setUpInputChannels");
final InputTransferToken inputTransferToken = new InputTransferToken();
- final InputChannel inputChannel = new InputChannel();
- final InputChannel sinkInputChannel = new InputChannel();
try {
windowSession.grantInputChannel(
displayId,
@@ -421,6 +431,9 @@ class DragResizeInputListener implements AutoCloseable {
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ // Removing this surface on the background thread to ensure that mInitInputChannels has
+ // already been finished.
+ mSurfaceControlTransactionSupplier.get().remove(mDecorationSurface).apply();
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 2d6f7459e0ae..732f04259fd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -173,6 +173,9 @@ class FluidResizeTaskPositioner implements TaskPositioner, Transitions.Transitio
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c544468f5191..9cc64ac9c276 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -108,19 +108,19 @@ class HandleMenu(
private val isViewAboveStatusBar: Boolean
get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
- private val pillElevation: Int = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_pill_elevation)
private val pillTopMargin: Int = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
- private val menuWidth = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_width) + pillElevation
+ R.dimen.desktop_mode_handle_menu_pill_spacing_margin
+ )
+ private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_width)
private val menuHeight = getHandleMenuHeight()
private val marginMenuTop = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_margin_top)
private val marginMenuStart = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_margin_start)
+ R.dimen.desktop_mode_handle_menu_margin_start
+ )
@VisibleForTesting
var handleMenuViewContainer: AdditionalViewContainer? = null
+
@VisibleForTesting
var handleMenuView: HandleMenuView? = null
@@ -139,7 +139,7 @@ class HandleMenu(
private val shouldShowMoreActionsPill: Boolean
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
- shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
private var loadAppInfoJob: Job? = null
@@ -243,7 +243,8 @@ class HandleMenu(
val y = handleMenuPosition.y.toInt()
handleMenuViewContainer =
if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue())
- || forceShowSystemBars) {
+ || forceShowSystemBars
+ ) {
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
taskId = taskInfo.taskId,
@@ -254,7 +255,11 @@ class HandleMenu(
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
view = handleMenuView.rootView,
- forciblyShownTypes = if (forceShowSystemBars) { systemBars() } else { 0 },
+ forciblyShownTypes = if (forceShowSystemBars) {
+ systemBars()
+ } else {
+ 0
+ },
ignoreCutouts = Flags.showAppHandleLargeScreens()
|| BubbleAnythingFlagHelper.enableBubbleToFullscreen()
)
@@ -372,7 +377,8 @@ class HandleMenu(
inputPoint.y - globalMenuPosition.y
)
if (splitScreenController.getSplitPosition(taskInfo.taskId)
- == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ) {
val leftStageBounds = Rect()
splitScreenController.getStageBounds(leftStageBounds, Rect())
inputRelativeToMenu.x += leftStageBounds.width().toFloat()
@@ -398,11 +404,11 @@ class HandleMenu(
* Determines handle menu height based the max size and the visibility of pills.
*/
private fun getHandleMenuHeight(): Int {
- var menuHeight = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_height) + pillElevation
+ var menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_height)
if (!shouldShowWindowingPill) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_windowing_pill_height)
+ R.dimen.desktop_mode_handle_menu_windowing_pill_height
+ )
menuHeight -= pillTopMargin
}
if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
@@ -422,14 +428,16 @@ class HandleMenu(
}
if (!shouldShowChangeAspectRatioButton) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height)
+ R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
+ )
}
if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
if (!shouldShowBrowserPill) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height)
+ R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height
+ )
menuHeight -= pillTopMargin
}
return menuHeight
@@ -472,48 +480,66 @@ class HandleMenu(
// Insets for ripple effect of App Info Pill. and Windowing Pill. buttons
val iconButtondrawableShiftInset = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift)
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift
+ )
val iconButtondrawableBaseInset = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base)
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base
+ )
private val iconButtonRippleRadius = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius)
- private val iconButtonDrawableInsetsBase = DrawableInsets(t = iconButtondrawableBaseInset,
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius
+ )
+ private val iconButtonDrawableInsetsBase = DrawableInsets(
+ t = iconButtondrawableBaseInset,
b = iconButtondrawableBaseInset, l = iconButtondrawableBaseInset,
- r = iconButtondrawableBaseInset)
- private val iconButtonDrawableInsetsLeft = DrawableInsets(t = iconButtondrawableBaseInset,
- b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0)
- private val iconButtonDrawableInsetsRight = DrawableInsets(t = iconButtondrawableBaseInset,
- b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset)
+ r = iconButtondrawableBaseInset
+ )
+ private val iconButtonDrawableInsetsLeft = DrawableInsets(
+ t = iconButtondrawableBaseInset,
+ b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0
+ )
+ private val iconButtonDrawableInsetsRight = DrawableInsets(
+ t = iconButtondrawableBaseInset,
+ b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset
+ )
// App Info Pill.
private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
- R.id.collapse_menu_button)
+ R.id.collapse_menu_button
+ )
+
@VisibleForTesting
val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+
@VisibleForTesting
val appNameView = appInfoPill.requireViewById<MarqueedTextView>(R.id.application_name)
// Windowing Pill.
private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
private val fullscreenBtn = windowingPill.requireViewById<ImageButton>(
- R.id.fullscreen_button)
+ R.id.fullscreen_button
+ )
private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
- R.id.split_screen_button)
+ R.id.split_screen_button
+ )
private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
private val floatingBtnSpace = windowingPill.requireViewById<Space>(
- R.id.floating_button_space)
+ R.id.floating_button_space
+ )
private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
private val desktopBtnSpace = windowingPill.requireViewById<Space>(
- R.id.desktop_button_space)
+ R.id.desktop_button_space
+ )
// More Actions Pill.
private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
- R.id.screenshot_button)
+ R.id.screenshot_button
+ )
private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
- R.id.new_window_button)
+ R.id.new_window_button
+ )
private val manageWindowBtn = moreActionsPill
.requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
@@ -521,11 +547,14 @@ class HandleMenu(
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
- R.id.open_in_app_or_browser_pill)
+ R.id.open_in_app_or_browser_pill
+ )
private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>(
- R.id.open_in_app_or_browser_button)
+ R.id.open_in_app_or_browser_button
+ )
private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
- R.id.open_by_default_button)
+ R.id.open_by_default_button
+ )
private val decorThemeUtil = DecorThemeUtil(context)
private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
@@ -734,9 +763,9 @@ class HandleMenu(
desktopBtn.imageTintList = style.windowingButtonColor
val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight
- else iconButtonDrawableInsetsLeft
+ else iconButtonDrawableInsetsLeft
val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft
- else iconButtonDrawableInsetsRight
+ else iconButtonDrawableInsetsRight
fullscreenBtn.apply {
background = createBackgroundDrawable(
@@ -808,9 +837,11 @@ class HandleMenu(
getString(R.string.open_in_browser_text)
}
+ val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button)
val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label)
val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image)
openInAppOrBrowserBtn.contentDescription = btnText
+ buttonRoot.contentDescription = btnText
label.apply {
text = btnText
setTextColor(style.textColor)
@@ -841,7 +872,7 @@ class HandleMenu(
*/
fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
- taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
index 4b2e473d6ec2..a723a7a4ac20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -23,9 +23,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
-import android.widget.TextView
import androidx.core.content.withStyledAttributes
-import androidx.core.view.isGone
import com.android.wm.shell.R
/**
@@ -54,6 +52,7 @@ class HandleMenuActionButton @JvmOverloads constructor(
context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+ rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
iconView.setImageResource(getResourceId(
R.styleable.HandleMenuActionButton_android_src, 0))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 1b0e0f70ed21..eb324f74ca82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -294,6 +294,10 @@ class MultiDisplayVeiledResizeTaskPositioner(
return Rect(repositionTaskBounds)
}
+ override fun close() {
+ displayController.removeDisplayWindowListener(this)
+ }
+
private fun resetVeilIfVisible() {
if (isResizingOrAnimatingResize) {
desktopWindowDecoration.hideResizeVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 63b288d133dd..06e5380fa1de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -41,4 +41,10 @@ public interface TaskDragResizer {
*/
void removeDragEventListener(
DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+ /**
+ * Releases any resources associated with this TaskDragResizer. This should be called when the
+ * associated window is closed.
+ */
+ void close();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index d2c79d76e6c1..7e941ec0d31a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -205,6 +205,9 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 1563259f4a1a..5e4a0a5860f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor;
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.window.TransitionInfo;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -83,12 +84,14 @@ public interface WindowDecorViewModel {
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
+ * @param changeMode the type of change to the task
*/
void onTaskChanging(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT);
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode);
/**
* Notifies that the given task is about to close to give the window decoration a chance to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 68e3d6e277e5..91a899c09407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -629,16 +629,32 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
*/
private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) {
mIsCaptionVisible = params.mIsCaptionVisible;
+ setCaptionVisibility(rootView, mIsCaptionVisible);
}
void setTaskDragResizer(TaskDragResizer taskDragResizer) {
mTaskDragResizer = taskDragResizer;
}
+ // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to
+ // keep implementation details more encapsulated.
+ private void setCaptionVisibility(View rootView, boolean visible) {
+ if (rootView == null) {
+ return;
+ }
+ final int v = visible ? View.VISIBLE : View.GONE;
+ final View captionView = rootView.findViewById(getCaptionViewId());
+ captionView.setVisibility(v);
+ }
+
int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
+ int getCaptionViewId() {
+ return Resources.ID_NULL;
+ }
+
/**
* Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
* registers {@link #mOnDisplaysChangedListener} if it doesn't.
@@ -683,6 +699,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
public void close() {
Trace.beginSection("WindowDecoration#close");
mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+ if (mTaskDragResizer != null) {
+ mTaskDragResizer.close();
+ }
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
releaseViews(wct);
mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 9c55f0ecda93..7c5f34f979cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -152,6 +152,8 @@ class DesktopTilingWindowDecoration(
endBounds = destinationBounds,
callback,
)
+ } else {
+ callback.invoke()
}
}
return isTiled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt
deleted file mode 100644
index f0a85306d177..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.windowdecor
-
-import android.animation.ObjectAnimator
-import android.view.View
-import android.view.View.Visibility
-import android.view.animation.PathInterpolator
-import android.widget.ImageButton
-import androidx.core.animation.doOnEnd
-import com.android.wm.shell.shared.animation.Interpolators
-
-/**
- * Animates the Desktop View's app handle.
- */
-class AppHandleAnimator(
- private val appHandleView: View,
- private val captionHandle: ImageButton,
-) {
- companion object {
- // Constants for animating the whole caption
- private const val APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS: Long = 275L
- private const val APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS: Long = 340
- private val APP_HANDLE_ANIMATION_INTERPOLATOR = PathInterpolator(
- 0.4f,
- 0f,
- 0.2f,
- 1f
- )
-
- // Constants for animating the caption's handle
- private const val HANDLE_ANIMATION_DURATION: Long = 100
- private val HANDLE_ANIMATION_INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN
- }
-
- private var animator: ObjectAnimator? = null
-
- /** Animates the given caption view to the given visibility after a visibility change. */
- fun animateVisibilityChange(@Visibility visible: Int) {
- when (visible) {
- View.VISIBLE -> animateShowAppHandle()
- else -> animateHideAppHandle()
- }
- }
-
- /** Animate appearance/disappearance of caption's handle. */
- fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
- cancel()
- animator = ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
- duration = HANDLE_ANIMATION_DURATION
- interpolator = HANDLE_ANIMATION_INTERPOLATOR
- start()
- }
- }
-
- private fun animateShowAppHandle() {
- cancel()
- appHandleView.alpha = 0f
- appHandleView.visibility = View.VISIBLE
- animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 1f).apply {
- duration = APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS
- interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
- start()
- }
- }
-
- private fun animateHideAppHandle() {
- cancel()
- animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 0f).apply {
- duration = APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS
- interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
- doOnEnd {
- appHandleView.visibility = View.GONE
- }
- start()
- }
- }
-
- /**
- * Cancels any active animations.
- */
- fun cancel() {
- animator?.removeAllListeners()
- animator?.cancel()
- animator = null
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 7ab3303be618..0985587a330e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.windowdecor.viewholder
+import android.animation.ObjectAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.res.ColorStateList
@@ -39,8 +40,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
-import com.android.wm.shell.windowdecor.AppHandleAnimator
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -48,7 +49,7 @@ import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystem
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
* It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
*/
-internal class AppHandleViewHolder(
+class AppHandleViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
@@ -56,19 +57,22 @@ internal class AppHandleViewHolder(
private val handler: Handler
) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) {
+ companion object {
+ private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
+ }
+
data class HandleData(
val taskInfo: RunningTaskInfo,
val position: Point,
val width: Int,
val height: Int,
- val isCaptionVisible: Boolean
+ val showInputLayer: Boolean
) : Data()
private lateinit var taskInfo: RunningTaskInfo
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
- private val animator: AppHandleAnimator = AppHandleAnimator(rootView, captionHandle)
private var statusBarInputLayerExists = false
// An invisible View that takes up the same coordinates as captionHandle but is layered
@@ -97,7 +101,7 @@ internal class AppHandleViewHolder(
}
override fun bindData(data: HandleData) {
- bindData(data.taskInfo, data.position, data.width, data.height, data.isCaptionVisible)
+ bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
}
private fun bindData(
@@ -105,15 +109,13 @@ internal class AppHandleViewHolder(
position: Point,
width: Int,
height: Int,
- isCaptionVisible: Boolean
+ showInputLayer: Boolean
) {
- setVisibility(isCaptionVisible)
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
// If handle is not in status bar region(i.e., bottom stage in vertical split),
// do not create an input layer
- if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible) {
+ if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) {
disposeStatusBarInputLayer()
return
}
@@ -129,11 +131,11 @@ internal class AppHandleViewHolder(
}
override fun onHandleMenuOpened() {
- animator.animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
+ animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
}
override fun onHandleMenuClosed() {
- animator.animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
+ animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
private fun createStatusBarInputLayer(handlePosition: Point,
@@ -237,16 +239,6 @@ internal class AppHandleViewHolder(
}
}
- private fun setVisibility(visible: Boolean) {
- val v = if (visible) View.VISIBLE else View.GONE
- if (captionView.visibility == v) return
- if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
- captionView.visibility = v
- return
- }
- animator.animateVisibilityChange(v)
- }
-
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
return if (shouldUseLightCaptionColors(taskInfo)) {
context.getColor(R.color.desktop_mode_caption_handle_bar_light)
@@ -272,7 +264,36 @@ internal class AppHandleViewHolder(
} ?: false
}
- override fun close() {
- animator.cancel()
+ /** Animate appearance/disappearance of caption handle as the handle menu is animated. */
+ private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
+ val animator =
+ ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
+ duration = CAPTION_HANDLE_ANIMATION_DURATION
+ interpolator = Interpolators.FAST_OUT_SLOW_IN
+ }
+ animator.start()
+ }
+
+ override fun close() {}
+
+ /** Factory class for creating [AppHandleViewHolder] objects. */
+ class Factory {
+ /**
+ * Create a [AppHandleViewHolder] object to handle caption view and status bar
+ * input layer logic.
+ */
+ fun create(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: OnClickListener,
+ windowManagerWrapper: WindowManagerWrapper,
+ handler: Handler,
+ ): AppHandleViewHolder = AppHandleViewHolder(
+ rootView,
+ onCaptionTouchListener,
+ onCaptionButtonClickListener,
+ windowManagerWrapper,
+ handler,
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
new file mode 100644
index 000000000000..68f7ef09ee70
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A TestRule to manage multiple simulated connected overlay displays.
+ */
+class SimulatedConnectedDisplayTestRule : TestRule {
+
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+ private val displayManager = context.getSystemService(DisplayManager::class.java)
+ private val addedDisplays = mutableListOf<Int>()
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ try {
+ base.evaluate()
+ } finally {
+ teardown()
+ }
+ }
+ }
+
+ private fun teardown() {
+ cleanupTestDisplays()
+ }
+
+ /**
+ * Adds multiple overlay displays with specified dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param displays A list of [Point] objects, where each [Point] represents the
+ * width and height of a simulated display.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(displays: List<Point>): List<Int> = runBlocking {
+ // Cleanup any existing overlay displays.
+ cleanupTestDisplays()
+
+ if (displays.isEmpty()) {
+ Log.w(TAG, "setupTestDisplays called with an empty list. No displays created.")
+ return@runBlocking emptyList()
+ }
+
+ val displayAddedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {}
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ val displaySettings = displays.joinToString(separator = ";") { size ->
+ "${size.x}x${size.y}/$DEFAULT_DENSITY"
+ }
+
+ // Add the overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+ )
+ withTimeoutOrNull(TIMEOUT) {
+ displayAddedFlow.take(displays.size).collect { displayId ->
+ addedDisplays.add(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be added.")
+ addedDisplays
+ }
+
+ /**
+ * Adds multiple overlay displays with default dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param count number of displays to add.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(count: Int): List<Int> {
+ val displays = List(count) { Point(DEFAULT_WIDTH, DEFAULT_HEIGHT) }
+ return setupTestDisplays(displays)
+ }
+
+ private fun cleanupTestDisplays() = runBlocking {
+ if (addedDisplays.isEmpty()) {
+ return@runBlocking
+ }
+
+ val displayRemovedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {}
+ override fun onDisplayRemoved(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ // Remove overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+
+ withTimeoutOrNull(TIMEOUT) {
+ displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+ addedDisplays.remove(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be removed.")
+ }
+
+ private companion object {
+ const val DEFAULT_WIDTH = 1280
+ const val DEFAULT_HEIGHT = 720
+ const val DEFAULT_DENSITY = 160
+ const val TAG = "SimulatedConnectedDisplayTestRule"
+ val TIMEOUT = 10.seconds
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 05750a54f566..b139c000a1a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -889,8 +889,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
*/
private void doStartEvents(int startX, int moveX) {
doMotionEvent(MotionEvent.ACTION_DOWN, startX);
- mController.onThresholdCrossed();
doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+ mController.onThresholdCrossed();
}
private void simulateRemoteAnimationStart() throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 2ef6c558b0b5..43bcc3b61124 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,8 +58,7 @@ public class BackProgressAnimatorTest extends ShellTestCase {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2cc52c5ab9ad..9d4cc49a7a65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,8 +224,7 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null
+ /* swipeEdge = */ BackEvent.EDGE_LEFT
)
private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7a7d88b80ce3..b3d2db6da6d5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -804,7 +803,7 @@ public class BubbleDataTest extends ShellTestCase {
mBubbleData.setListener(mListener);
changeExpandedStateAtTime(true, 2000L);
- verifyZeroInteractions(mListener);
+ verifyNoMoreInteractions(mListener);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 925ca0f1638d..b136bed3c942 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
@@ -52,6 +53,7 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestSyncExecutor;
+import com.android.wm.shell.bubbles.BubbleTransitions.DraggedBubbleIconToFullscreen;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.ShellExecutor;
@@ -136,6 +138,7 @@ public class BubbleTransitionsTest extends ShellTestCase {
when(tvtc.getTaskInfo()).thenReturn(taskInfo);
when(tv.getController()).thenReturn(tvtc);
when(mBubble.getTaskView()).thenReturn(tv);
+ when(tv.getTaskInfo()).thenReturn(taskInfo);
mRepository.add(tvtc);
return taskInfo;
}
@@ -285,4 +288,62 @@ public class BubbleTransitionsTest extends ShellTestCase {
// directly tested.
assertFalse(mTaskViewTransitions.hasPending());
}
+
+ @Test
+ public void convertDraggedBubbleToFullscreen() {
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final DraggedBubbleIconToFullscreen bt =
+ (DraggedBubbleIconToFullscreen) mBubbleTransitions
+ .startDraggedBubbleIconToFullscreen(mBubble);
+ verify(mTransitions).startTransition(anyInt(), any(), eq(bt));
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setMode(TRANSIT_TO_FRONT);
+ chg.setTaskInfo(taskInfo);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCb = wct -> {};
+ bt.startAnimation(bt.mTransition, info, startT, finishT, finishCb);
+ verify(startT).apply();
+ assertFalse(mTaskViewTransitions.hasPending());
+ }
+
+ @Test
+ public void convertFloatingBubbleToFullscreen() {
+ final BubbleExpandedView bev = mock(BubbleExpandedView.class);
+ final ViewRootImpl vri = mock(ViewRootImpl.class);
+ when(bev.getViewRootImpl()).thenReturn(vri);
+ when(mBubble.getBubbleBarExpandedView()).thenReturn(null);
+ when(mBubble.getExpandedView()).thenReturn(bev);
+
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+ mBubble, taskInfo);
+ final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+ verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+ verify(mBubble).setPreparingTransition(eq(bt));
+ assertTrue(mTaskViewTransitions.hasPending());
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setMode(TRANSIT_CHANGE);
+ chg.setTaskInfo(taskInfo);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCb = wct -> {};
+ cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+ // Can really only verify that it interfaces with the taskViewTransitions queue.
+ // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+ // in order to properly synchronize surface manipulation with drawing and thus can't be
+ // directly tested.
+ assertFalse(mTaskViewTransitions.hasPending());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index c4b9c9ba43f1..b4b96791298d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
@@ -84,7 +83,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onMove(0, -600);
// Check that velocity up is about 5000
verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -94,8 +93,8 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
- verifyZeroInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -107,7 +106,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onMove(100, 0);
verify(mMotionEventListener).onUp(0, 0);
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -119,7 +118,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -129,7 +128,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onCancel();
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
private MotionEvent newEvent(int actionDown, float x, float y) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index 3323740697f3..1472464e8143 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
@@ -105,6 +105,6 @@ public class DevicePostureControllerTest extends ShellTestCase {
int sameDevicePosture = mDevicePostureCaptor.getValue();
mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
- verifyZeroInteractions(mOnDevicePostureChangedListener);
+ verifyNoMoreInteractions(mOnDevicePostureChangedListener);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ee9d17706372..a53277a3764e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -32,7 +32,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.Insets;
import android.graphics.Point;
@@ -108,26 +108,26 @@ public class DisplayImeControllerTest extends ShellTestCase {
public void insetsControlChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void insetsChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsChanged(insetsStateWithIme(false));
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
// With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
@@ -135,7 +135,7 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void reappliesVisibilityToChangedLeash() {
- verifyZeroInteractions(mT);
+ verifyNoMoreInteractions(mT);
mPerDisplay.mImeShowing = false;
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
index 96d202ce3a85..7d1866975848 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -145,7 +145,7 @@ public class TabletopModeControllerTest extends ShellTestCase {
mConfiguration.windowConfiguration.setDisplayRotation(Surface.ROTATION_0);
mPipTabletopController.onDisplayConfigurationChanged(DEFAULT_DISPLAY, mConfiguration);
- verifyZeroInteractions(mOnTabletopModeChangedListener);
+ verifyNoMoreInteractions(mOnTabletopModeChangedListener);
}
// Test cases starting from folded state (DEVICE_POSTURE_CLOSED)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index e92e243172f7..a2066dbf7a5f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -21,7 +21,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -157,7 +157,7 @@ public class PipAppOpsListenerTest {
opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
packageName);
- verifyZeroInteractions(mMockExecutor);
+ verifyNoMoreInteractions(mMockExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
index 4cdb1e5b5d10..25dbc64f83de 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_PIP2;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -119,12 +120,28 @@ public class PipDesktopStateTest {
}
@Test
- @EnableFlags(FLAG_ENABLE_CONNECTED_DISPLAYS_PIP)
+ @EnableFlags({
+ FLAG_ENABLE_CONNECTED_DISPLAYS_PIP, FLAG_ENABLE_PIP2
+ })
public void isConnectedDisplaysPipEnabled_returnsTrue() {
assertTrue(mPipDesktopState.isConnectedDisplaysPipEnabled());
}
@Test
+ public void isPipInDesktopMode_anyDeskActive_returnsTrue() {
+ when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
+
+ assertTrue(mPipDesktopState.isPipInDesktopMode());
+ }
+
+ @Test
+ public void isPipInDesktopMode_noDeskActive_returnsFalse() {
+ when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false);
+
+ assertFalse(mPipDesktopState.isPipInDesktopMode());
+ }
+
+ @Test
public void getOutPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() {
when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDoubleTapHelperTest.java
index 1756aad8fc9b..cc23d9a75fcd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDoubleTapHelperTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDoubleTapHelperTest.java
@@ -21,7 +21,6 @@ import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_DEFAU
import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_MAX;
import static com.android.wm.shell.common.pip.PipDoubleTapHelper.nextSizeSpec;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.graphics.Point;
@@ -41,131 +40,75 @@ import org.mockito.Mock;
*/
@RunWith(AndroidTestingRunner.class)
public class PipDoubleTapHelperTest extends ShellTestCase {
- // represents the current pip window state and has information on current
- // max, min, and normal sizes
- @Mock private PipBoundsState mBoundStateMock;
- // tied to boundsStateMock.getBounds() in setUp()
- @Mock private Rect mBoundsMock;
-
- // represents the most recent manually resized bounds
- // i.e. dimensions from the most recent pinch in/out
- @Mock private Rect mUserResizeBoundsMock;
-
- // actual dimensions of the pip screen bounds
- private static final int MAX_WIDTH = 100;
- private static final int DEFAULT_WIDTH = 40;
- private static final int MIN_WIDTH = 10;
-
- private static final int AVERAGE_WIDTH = (MAX_WIDTH + MIN_WIDTH) / 2;
-
- /**
- * Initializes mocks and assigns values for different pip screen bounds.
- */
+ @Mock private PipBoundsState mMockPipBoundsState;
+
+ // Actual dimension guidelines of the PiP bounds.
+ private static final int MAX_EDGE_SIZE = 100;
+ private static final int DEFAULT_EDGE_SIZE = 60;
+ private static final int MIN_EDGE_SIZE = 50;
+ private static final int AVERAGE_EDGE_SIZE = (MAX_EDGE_SIZE + MIN_EDGE_SIZE) / 2;
+
@Before
public void setUp() {
// define pip bounds
- when(mBoundStateMock.getMaxSize()).thenReturn(new Point(MAX_WIDTH, 20));
- when(mBoundStateMock.getMinSize()).thenReturn(new Point(MIN_WIDTH, 2));
+ when(mMockPipBoundsState.getMaxSize()).thenReturn(new Point(MAX_EDGE_SIZE, MAX_EDGE_SIZE));
+ when(mMockPipBoundsState.getMinSize()).thenReturn(new Point(MIN_EDGE_SIZE, MIN_EDGE_SIZE));
- Rect rectMock = mock(Rect.class);
- when(rectMock.width()).thenReturn(DEFAULT_WIDTH);
- when(mBoundStateMock.getNormalBounds()).thenReturn(rectMock);
+ final Rect normalBounds = new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE);
+ when(mMockPipBoundsState.getNormalBounds()).thenReturn(normalBounds);
+ }
- when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
- when(mBoundStateMock.getBounds()).thenReturn(mBoundsMock);
+ @Test
+ public void nextSizeSpec_resizedWiderThanAverage_returnDefaultThenCustom() {
+ final int resizeEdgeSize = (MAX_EDGE_SIZE + AVERAGE_EDGE_SIZE) / 2;
+ final Rect userResizeBounds = new Rect(0, 0, resizeEdgeSize, resizeEdgeSize);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);
+
+ // once we toggle to DEFAULT only PiP bounds state gets updated - not the user resize bounds
+ when(mMockPipBoundsState.getBounds()).thenReturn(
+ new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE));
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_CUSTOM);
+ }
+
+ @Test
+ public void nextSizeSpec_resizedSmallerThanAverage_returnMaxThenCustom() {
+ final int resizeEdgeSize = (AVERAGE_EDGE_SIZE + MIN_EDGE_SIZE) / 2;
+ final Rect userResizeBounds = new Rect(0, 0, resizeEdgeSize, resizeEdgeSize);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);
+
+ // Once we toggle to MAX our screen size gets updated but not the user resize bounds
+ when(mMockPipBoundsState.getBounds()).thenReturn(
+ new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE));
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_CUSTOM);
}
- /**
- * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
- *
- * <p>when the user resizes the screen to a larger than the average but not the maximum width,
- * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.DEFAULT}
- */
@Test
- public void testNextScreenSize_resizedWiderThanAverage_returnDefaultThenCustom() {
- // make the user resize width in between MAX and average
- when(mUserResizeBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);
- // make current bounds same as resized bound since no double tap yet
- when(mBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to DEFAULT state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_DEFAULT);
-
- // once we toggle to DEFAULT our screen size gets updated
- // but not the user resize bounds
- when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to CUSTOM state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_CUSTOM);
+ public void nextSizeSpec_resizedToMax_returnDefault() {
+ final Rect userResizeBounds = new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);
}
- /**
- * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
- *
- * <p>when the user resizes the screen to a smaller than the average but not the default width,
- * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.MAX}
- */
@Test
- public void testNextScreenSize_resizedNarrowerThanAverage_returnMaxThenCustom() {
- // make the user resize width in between MIN and average
- when(mUserResizeBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);
- // make current bounds same as resized bound since no double tap yet
- when(mBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to MAX state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_MAX);
-
- // once we toggle to MAX our screen size gets updated
- // but not the user resize bounds
- when(mBoundsMock.width()).thenReturn(MAX_WIDTH);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to CUSTOM state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_CUSTOM);
+ public void nextSizeSpec_resizedToDefault_returnMax() {
+ final Rect userResizeBounds = new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);
}
- /**
- * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
- *
- * <p>when the user resizes the screen to exactly the maximum width
- * then we toggle to {@code PipSizeSpec.DEFAULT}
- */
@Test
- public void testNextScreenSize_resizedToMax_returnDefault() {
- // the resized width is the same as MAX_WIDTH
- when(mUserResizeBoundsMock.width()).thenReturn(MAX_WIDTH);
- // the current bounds are also at MAX_WIDTH
- when(mBoundsMock.width()).thenReturn(MAX_WIDTH);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to DEFAULT state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_DEFAULT);
+ public void nextSizeSpec_resizedToAlmostMax_returnDefault() {
+ final Rect userResizeBounds = new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE - 1);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);
}
- /**
- * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
- *
- * <p>when the user resizes the screen to exactly the default width
- * then we toggle to {@code PipSizeSpec.MAX}
- */
@Test
- public void testNextScreenSize_resizedToDefault_returnMax() {
- // the resized width is the same as DEFAULT_WIDTH
- when(mUserResizeBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
- // the current bounds are also at DEFAULT_WIDTH
- when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
-
- // then nextScreenSize() i.e. double tapping should
- // toggle to MAX state
- Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
- SIZE_SPEC_MAX);
+ public void nextSizeSpec_resizedToAlmostMin_returnMax() {
+ final Rect userResizeBounds = new Rect(0, 0, MIN_EDGE_SIZE, MIN_EDGE_SIZE + 1);
+ when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
+ Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 275d7b73a112..2aebcdcc3bf5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -61,10 +61,10 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var displayController: DisplayController
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
- @Mock private lateinit var mockDesktopRepositoryInitializer: DesktopRepositoryInitializer
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
@Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
+ private val desktopRepositoryInitializer = FakeDesktopRepositoryInitializer()
private val testScope = TestScope()
private lateinit var mockitoSession: StaticMockitoSession
@@ -90,7 +90,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
shellInit,
testScope.backgroundScope,
displayController,
- mockDesktopRepositoryInitializer,
+ desktopRepositoryInitializer,
mockDesktopUserRepositories,
mockDesktopTasksController,
desktopDisplayModeController,
@@ -109,12 +109,10 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Test
fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() =
testScope.runTest {
- val stateFlow = MutableStateFlow(false)
- whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- stateFlow.emit(true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
runCurrent()
verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
@@ -123,8 +121,6 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Test
fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() =
testScope.runTest {
- val stateFlow = MutableStateFlow(false)
- whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
@@ -136,13 +132,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Test
fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() =
testScope.runTest {
- val stateFlow = MutableStateFlow(false)
- whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- stateFlow.emit(true)
- stateFlow.emit(true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
runCurrent()
verify(mockDesktopTasksController, times(1)).createDesk(DEFAULT_DISPLAY)
@@ -151,13 +145,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Test
fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() =
testScope.runTest {
- val stateFlow = MutableStateFlow(false)
- whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
- stateFlow.emit(true)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
runCurrent()
verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
@@ -203,4 +195,15 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
verify(desktopDisplayModeController).refreshDisplayWindowingMode()
}
+
+ private class FakeDesktopRepositoryInitializer : DesktopRepositoryInitializer {
+ override var deskRecreationFactory: DesktopRepositoryInitializer.DeskRecreationFactory =
+ DesktopRepositoryInitializer.DeskRecreationFactory { _, _, deskId -> deskId }
+
+ override val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override fun initialize(userRepositories: DesktopUserRepositories) {
+ isInitialized.value = true
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 450989dd334d..da6a67c679ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,28 +26,36 @@ import android.os.Binder
import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.UsesFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +68,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
/**
* Test class for [DesktopDisplayModeController]
@@ -68,6 +77,7 @@ import org.mockito.kotlin.whenever
*/
@SmallTest
@RunWith(TestParameterInjector::class)
+@UsesFlags(com.android.server.display.feature.flags.Flags::class)
class DesktopDisplayModeControllerTest(
@TestParameter(valuesProvider = FlagsParameterizationProvider::class)
flags: FlagsParameterization
@@ -79,6 +89,7 @@ class DesktopDisplayModeControllerTest(
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
private val inputManager = mock<InputManager>()
+ private val displayController = mock<DisplayController>()
private val mainHandler = mock<Handler>()
private lateinit var controller: DesktopDisplayModeController
@@ -90,6 +101,13 @@ class DesktopDisplayModeControllerTest(
TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
private val wallpaperToken = MockToken().token()
+ private val defaultDisplay = mock<Display>()
+ private val externalDisplay = mock<Display>()
+
+ private lateinit var extendedDisplaySettingsRestoreSession:
+ ExtendedDisplaySettingsRestoreSession
+
+ private lateinit var mockitoSession: StaticMockitoSession
init {
mSetFlagsRule.setFlagsParameterization(flags)
@@ -97,6 +115,13 @@ class DesktopDisplayModeControllerTest(
@Before
fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ extendedDisplaySettingsRestoreSession =
+ ExtendedDisplaySettingsRestoreSession(context.contentResolver)
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.thenReturn(defaultTDA)
@@ -109,42 +134,57 @@ class DesktopDisplayModeControllerTest(
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler,
)
runningTasks.add(freeformTask)
runningTasks.add(fullscreenTask)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
+ whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
setTabletModeStatus(SwitchState.UNKNOWN)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
+ )
+ ).thenReturn(true)
+ }
+
+ @After
+ fun tearDown() {
+ extendedDisplaySettingsRestoreSession.restore()
+ mockitoSession.finishMocking()
}
private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
- if (expectToSwitch) {
- // Assumes [connectExternalDisplay] properly triggered the switching transition.
- // Will verify the transition later along with [disconnectExternalDisplay].
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- }
- disconnectExternalDisplay()
+ setExtendedMode(true)
+
+ connectExternalDisplay()
+ if (expectToSwitch) {
+ // Assumes [connectExternalDisplay] properly triggered the switching transition.
+ // Will verify the transition later along with [disconnectExternalDisplay].
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ }
+ disconnectExternalDisplay()
- if (expectToSwitch) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
+ if (expectToSwitch) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
}
}
@@ -176,15 +216,16 @@ class DesktopDisplayModeControllerTest(
disconnectExternalDisplay()
}
setTabletModeStatus(tabletModeStatus)
-
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
+ setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
)
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -199,15 +240,16 @@ class DesktopDisplayModeControllerTest(
disconnectExternalDisplay()
}
setTabletModeStatus(param.tabletModeStatus)
-
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
+ setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
)
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -215,18 +257,16 @@ class DesktopDisplayModeControllerTest(
fun displayWindowingModeSwitch_existingTasksOnConnected() {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
+ connectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
@@ -236,18 +276,16 @@ class DesktopDisplayModeControllerTest(
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
WINDOWING_MODE_FULLSCREEN
}
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- disconnectExternalDisplay()
+ disconnectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
}
private fun connectExternalDisplay() {
@@ -266,18 +304,30 @@ class DesktopDisplayModeControllerTest(
whenever(inputManager.isInTabletMode()).thenReturn(status.value)
}
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int,
- ) : AutoCloseable {
+ private fun setExtendedMode(enabled: Boolean) {
+ if (DisplayFlags.enableDisplayContentModeManagement()) {
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ externalDisplay
+ )
+ ).thenReturn(enabled)
+ } else {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ if (enabled) 1 else 0,
+ )
+ }
+ }
+
+ private class ExtendedDisplaySettingsRestoreSession(
+ private val contentResolver: ContentResolver
+ ) {
private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
- init {
- Settings.Global.putInt(contentResolver, settingName, overrideValue)
- }
-
- override fun close() {
+ fun restore() {
Settings.Global.putInt(contentResolver, settingName, initialValue)
}
}
@@ -287,7 +337,8 @@ class DesktopDisplayModeControllerTest(
context: TestParameterValuesProvider.Context
): List<FlagsParameterization> {
return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH
+ Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+ DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
)
}
}
@@ -305,54 +356,119 @@ class DesktopDisplayModeControllerTest(
val defaultWindowingMode: Int,
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- FREEFORM_EXTERNAL_EXTENDED(
+ FREEFORM_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_EXTENDED_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FREEFORM_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_MIRROR_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_MIRROR_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_MIRROR_NO_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_EXTENDED(
+ FULLSCREEN_NO_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FREEFORM_NO_EXTERNAL_EXTENDED(
+ FULLSCREEN_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_EXTENDED_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_EXTENDED(
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_EXTERNAL_MIRROR(
+ FREEFORM_EXTERNAL_MIRROR_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_MIRROR(
+ FULLSCREEN_EXTERNAL_MIRROR_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_NO_EXTERNAL_MIRROR(
+ FREEFORM_NO_EXTERNAL_MIRROR_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_MIRROR(
+ FULLSCREEN_NO_EXTERNAL_MIRROR_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
@@ -361,78 +477,175 @@ class DesktopDisplayModeControllerTest(
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
val tabletModeStatus: SwitchState,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET(
+ EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET(
+ NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET(
+ EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET(
+ NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL(
+ EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL(
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_MIRROR_CLAMSHELL(
+ EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL(
+ NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN(
+ EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN(
+ NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN(
+ EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN(
+ NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 8a5acfa70f50..695cf600b359 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -23,7 +23,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
-import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
@@ -102,7 +102,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -127,7 +127,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -135,7 +135,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -157,7 +157,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
}
@@ -166,7 +166,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -205,7 +205,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -213,7 +213,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -252,7 +252,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -260,7 +260,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -302,7 +302,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -346,7 +346,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -390,7 +390,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -434,7 +434,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(FocusReason.UNKNOWN.reason),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -446,7 +446,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -485,7 +485,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index b7d25b5255f8..bd37610ae65b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -83,7 +83,7 @@ import org.mockito.kotlin.same
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -596,7 +596,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskRemoved(
eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -668,7 +668,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -701,7 +701,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -729,7 +729,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -753,7 +753,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
// task 2 resize
val newTaskInfo2 =
@@ -781,7 +781,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -892,14 +892,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
eq(taskUpdate.visibleTaskCount.toString()),
)
}
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
assertFalse(transitionObserver.isSessionActive)
verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
new file mode 100644
index 000000000000..ef394d81cc57
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.TRANSIT_PIP
+import android.window.TransitionInfo
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [DesktopPipTransitionObserver].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionObserverTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopPipTransitionObserverTest : ShellTestCase() {
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ private lateinit var observer: DesktopPipTransitionObserver
+
+ private val transition = Binder()
+ private var onSuccessInvokedCount = 0
+
+ @Before
+ fun setUp() {
+ observer = DesktopPipTransitionObserver()
+
+ onSuccessInvokedCount = 0
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onTransitionReady_taskInPinnedWindowingMode_onSuccessInvoked() {
+ val taskId = 1
+ val pipTransition = createPendingPipTransition(taskId)
+ val successfulChange = createChange(taskId, WINDOWING_MODE_PINNED)
+ observer.addPendingPipTransition(pipTransition)
+
+ observer.onTransitionReady(
+ transition = transition,
+ info = TransitionInfo(
+ TRANSIT_PIP, /* flags= */
+ 0
+ ).apply { addChange(successfulChange) },
+ )
+
+ assertThat(onSuccessInvokedCount).isEqualTo(1)
+ }
+
+ private fun createPendingPipTransition(
+ taskId: Int
+ ): DesktopPipTransitionObserver.PendingPipTransition {
+ return DesktopPipTransitionObserver.PendingPipTransition(
+ token = transition,
+ taskId = taskId,
+ onSuccess = { onSuccessInvokedCount += 1 },
+ )
+ }
+
+ private fun createChange(taskId: Int, windowingMode: Int): TransitionInfo.Change {
+ return TransitionInfo.Change(mock(), mock()).apply {
+ taskInfo =
+ TestRunningTaskInfoBuilder()
+ .setTaskId(taskId)
+ .setWindowingMode(windowingMode)
+ .build()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index f84a1a38bdfc..a10aeca95bce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -275,6 +275,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeActiveTask_excludingDesk_leavesTaskInDesk() {
+ repo.addDesk(displayId = 2, deskId = 11)
+ repo.addDesk(displayId = 3, deskId = 12)
+ repo.addTaskToDesk(displayId = 3, deskId = 12, taskId = 100, isVisible = true)
+
+ repo.removeActiveTask(taskId = 100, excludedDeskId = 12)
+
+ assertThat(repo.getActiveTaskIdsInDesk(12)).contains(100)
+ }
+
+ @Test
fun isActiveTask_nonExistingTask_returnsFalse() {
assertThat(repo.isActiveTask(99)).isFalse()
}
@@ -1225,36 +1237,6 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
- fun setTaskInPip_savedAsMinimizedPipInDisplay() {
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isFalse()
-
- repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
-
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
- }
-
- @Test
- fun removeTaskInPip_removedAsMinimizedPipInDisplay() {
- repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
-
- repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = false)
-
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isFalse()
- }
-
- @Test
- fun setTaskInPip_multipleDisplays_bothAreInPip() {
- repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
- repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
- repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
- repo.setTaskInPip(SECOND_DISPLAY, taskId = 2, enterPip = true)
-
- assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
- assertThat(repo.isTaskMinimizedPipInDisplay(SECOND_DISPLAY, taskId = 2)).isTrue()
- }
-
- @Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun addTask_deskDoesNotExists_createsDesk() {
repo.addTask(displayId = 999, taskId = 6, isVisible = true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index b2553b0bd30f..01ea986d3260 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -57,6 +57,7 @@ import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableContext
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
import android.view.DragEvent
import android.view.Gravity
import android.view.MotionEvent
@@ -264,6 +265,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Mock private lateinit var desksOrganizer: DesksOrganizer
@Mock private lateinit var userProfileContexts: UserProfileContexts
@Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
+ @Mock private lateinit var desktopPipTransitionObserver: DesktopPipTransitionObserver
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var mockDisplayContext: Context
@Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
@@ -392,6 +394,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
whenever(userProfileContexts[anyInt()]).thenReturn(context)
whenever(userProfileContexts.getOrCreate(anyInt())).thenReturn(context)
+ whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(Binder())
controller = createController()
controller.setSplitScreenController(splitScreenController)
@@ -456,6 +459,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionsObserver,
+ Optional.of(desktopPipTransitionObserver),
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -3123,6 +3127,36 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimit() {
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ val targetDeskTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ }
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+ controller.moveToNextDisplay(task.taskId)
+
+ val wct = getLatestTransition()
+ assertNotNull(wct)
+ verify(desksOrganizer).minimizeTask(wct, targetDeskId, targetDeskTasks[0])
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
@@ -3191,10 +3225,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
verify(desksOrganizer).activateDesk(any(), eq(targetDeskId))
verify(desksTransitionsObserver)
.addPendingTransition(
- DeskTransition.ActivateDesk(
+ DeskTransition.ActiveDeskWithTask(
token = transition,
displayId = SECOND_DISPLAY,
deskId = targetDeskId,
+ enterTaskId = task.taskId,
)
)
}
@@ -3470,6 +3505,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
val task = setUpPipTask(autoEnterEnabled = true)
val handler = mock(TransitionHandler::class.java)
@@ -3484,6 +3520,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
fun onPipTaskMinimize_autoEnterDisabled_startMinimizeTransition() {
val task = setUpPipTask(autoEnterEnabled = false)
whenever(
@@ -3503,6 +3540,103 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+ val handler = mock(TransitionHandler::class.java)
+ whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
+ .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
+
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+ )
+ fun onDesktopTaskEnteredPip_pipIsLastTask_removesWallpaper() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+
+ controller.onDesktopTaskEnteredPip(
+ taskId = task.taskId,
+ deskId = DEFAULT_DISPLAY,
+ displayId = task.displayId,
+ taskIsLastVisibleTaskBeforePip = true,
+ )
+
+ // Wallpaper is moved to the back
+ val wct = getLatestTransition()
+ wct.assertReorder(wallpaperToken, /* toTop= */ false)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun onDesktopTaskEnteredPip_pipIsLastTask_deactivatesDesk() {
+ val deskId = DEFAULT_DISPLAY
+ val task = setUpPipTask(autoEnterEnabled = true, deskId = deskId)
+ val transition = Binder()
+ whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+ controller.onDesktopTaskEnteredPip(
+ taskId = task.taskId,
+ deskId = deskId,
+ displayId = task.displayId,
+ taskIsLastVisibleTaskBeforePip = true,
+ )
+
+ verify(desksOrganizer).deactivateDesk(any(), eq(deskId))
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onDesktopTaskEnteredPip_pipIsLastTask_launchesHome() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+
+ controller.onDesktopTaskEnteredPip(
+ taskId = task.taskId,
+ deskId = DEFAULT_DISPLAY,
+ displayId = task.displayId,
+ taskIsLastVisibleTaskBeforePip = true,
+ )
+
+ val wct = getLatestTransition()
+ wct.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onDesktopTaskEnteredPip_pipIsNotLastTask_doesntExitDesktopMode() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+ val deskId = DEFAULT_DISPLAY
+ setUpFreeformTask(deskId = deskId) // launch another freeform task
+ val transition = Binder()
+ whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+ controller.onDesktopTaskEnteredPip(
+ taskId = task.taskId,
+ deskId = deskId,
+ displayId = task.displayId,
+ taskIsLastVisibleTaskBeforePip = false,
+ )
+
+ // No transition to exit Desktop mode is started
+ verifyWCTNotExecuted()
+ verify(desktopModeEnterExitTransitionListener, never())
+ .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ verify(desksOrganizer, never()).deactivateDesk(any(), eq(deskId))
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
val task = setUpFreeformTask(active = true)
val transition = Binder()
@@ -3720,6 +3854,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun onDesktopWindowMinimize_sendsTaskbarRoundingUpdate() {
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val transition = Binder()
+ whenever(
+ freeformTaskTransitionStarter.startMinimizedModeTransition(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(transition)
+
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTask_switchToDesktop_movesTaskToDesk() {
taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = 5)
@@ -4983,6 +5135,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -7320,6 +7473,14 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testCreateDesk_invalidDisplay_dropsRequest() {
+ controller.createDesk(INVALID_DISPLAY)
+
+ verify(desksOrganizer, never()).createDesk(any(), any())
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
@@ -7615,8 +7776,12 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
return task
}
- private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo =
- setUpFreeformTask().apply {
+ private fun setUpPipTask(
+ autoEnterEnabled: Boolean,
+ displayId: Int = DEFAULT_DISPLAY,
+ deskId: Int = DEFAULT_DISPLAY,
+ ): RunningTaskInfo =
+ setUpFreeformTask(displayId = displayId, deskId = deskId).apply {
pictureInPictureParams =
PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index a7dc706eb6c9..5ef1ace7873d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -22,7 +22,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.os.Binder
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -30,7 +29,6 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_OPEN
-import android.view.WindowManager.TRANSIT_PIP
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.IWindowContainerToken
@@ -41,7 +39,6 @@ import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
-import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
import com.android.wm.shell.MockToken
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.back.BackAnimationController
@@ -51,10 +48,9 @@ import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpape
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
-import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -90,6 +86,7 @@ class DesktopTasksTransitionObserverTest {
private val userRepositories = mock<DesktopUserRepositories>()
private val taskRepository = mock<DesktopRepository>()
private val mixedHandler = mock<DesktopMixedTransitionHandler>()
+ private val pipTransitionObserver = mock<DesktopPipTransitionObserver>()
private val backAnimationController = mock<BackAnimationController>()
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
@@ -114,6 +111,7 @@ class DesktopTasksTransitionObserverTest {
transitions,
shellTaskOrganizer,
mixedHandler,
+ Optional.of(pipTransitionObserver),
backAnimationController,
desktopWallpaperActivityTokenProvider,
shellInit,
@@ -336,67 +334,61 @@ class DesktopTasksTransitionObserverTest {
}
@Test
- fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
- val wallpaperTask = createWallpaperTaskInfo()
-
- transitionObserver.onTransitionReady(
- transition = mock(),
- info = createCloseTransition(wallpaperTask),
- startTransaction = mock(),
- finishTransaction = mock(),
- )
-
- verify(desktopWallpaperActivityTokenProvider).removeToken(wallpaperTask.displayId)
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun pendingPipTransitionAborted_taskRepositoryOnPipAbortedInvoked() {
- val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
- val pipTransition = Binder()
- whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskOpened_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
transitionObserver.onTransitionReady(
- transition = pipTransition,
- info = createOpenChangeTransition(task, type = TRANSIT_PIP),
+ transition = mockTransition,
+ info = createOpenChangeTransition(nonTopTransparentTask),
startTransaction = mock(),
finishTransaction = mock(),
)
- transitionObserver.onTransitionFinished(transition = pipTransition, aborted = true)
- verify(taskRepository).onPipAborted(task.displayId, task.taskId)
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
}
@Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun exitPipTransition_taskRepositoryClearTaskInPip() {
- val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
- whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskSentToFront_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
transitionObserver.onTransitionReady(
- transition = mock(),
- info = createOpenChangeTransition(task, type = TRANSIT_EXIT_PIP),
+ transition = mockTransition,
+ info = createToFrontTransition(nonTopTransparentTask),
startTransaction = mock(),
finishTransaction = mock(),
)
- verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
}
@Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun removePipTransition_taskRepositoryClearTaskInPip() {
- val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
- whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
+ fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
+ val wallpaperTask = createWallpaperTaskInfo()
transitionObserver.onTransitionReady(
transition = mock(),
- info = createOpenChangeTransition(task, type = TRANSIT_REMOVE_PIP),
+ info = createCloseTransition(wallpaperTask),
startTransaction = mock(),
finishTransaction = mock(),
)
- verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
+ verify(desktopWallpaperActivityTokenProvider).removeToken(wallpaperTask.displayId)
}
private fun createBackNavigationTransition(
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 6e7adf368155..4e2994c27729 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
@@ -54,7 +54,9 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.MockitoSession
@@ -64,7 +66,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -85,8 +87,17 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
@Mock private lateinit var desktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var bubbleController: BubbleController
@Mock private lateinit var visualIndicator: DesktopModeVisualIndicator
-
- private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+ @Mock private lateinit var dragCancelCallback: Runnable
+ @Mock
+ private lateinit var dragToDesktopStateListener:
+ DragToDesktopTransitionHandler.DragToDesktopStateListener
+
+ private val transactionSupplier = Supplier {
+ val transaction = mock<SurfaceControl.Transaction>()
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+ transaction
+ }
private lateinit var defaultHandler: DragToDesktopTransitionHandler
private lateinit var springHandler: SpringDragToDesktopTransitionHandler
@@ -104,7 +115,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
Optional.of(bubbleController),
transactionSupplier,
)
- .apply { setSplitScreenController(splitScreenController) }
+ .apply {
+ setSplitScreenController(splitScreenController)
+ dragToDesktopStateListener =
+ this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+ }
springHandler =
SpringDragToDesktopTransitionHandler(
context,
@@ -115,7 +130,11 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
Optional.of(bubbleController),
transactionSupplier,
)
- .apply { setSplitScreenController(splitScreenController) }
+ .apply {
+ setSplitScreenController(splitScreenController)
+ dragToDesktopStateListener =
+ this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+ }
mockitoSession =
ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
@@ -438,7 +457,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
// No need to animate the cancel since the start animation couldn't even start.
- verifyZeroInteractions(dragAnimator)
+ verifyNoMoreInteractions(dragAnimator)
}
@Test
@@ -489,7 +508,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
// Should NOT have any transaction changes
- verifyZeroInteractions(mergedStartTransaction)
+ verifyNoMoreInteractions(mergedStartTransaction)
// Should NOT merge animation
verify(finishCallback, never()).onTransitionFinished(any())
}
@@ -706,8 +725,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
- fun mergeOtherTransition_cancelAndEndNotYetRequested_doesntInterruptsStartDrag() {
+ @DisableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_flagDisabled_cancelAndEndNotYetRequested_doesNotInterruptStartDrag() {
val finishCallback = mock<Transitions.TransitionFinishCallback>()
val task = createTask()
defaultHandler.onTaskResizeAnimationListener = mock()
@@ -721,6 +740,39 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_cancelAndEndNotYetRequested_interruptsStartDrag() {
+ val finishCallback = mock<Transitions.TransitionFinishCallback>()
+ val task = createTask()
+ defaultHandler.onTaskResizeAnimationListener = mock()
+ val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+ mergeInterruptingTransition(mergeTarget = startTransition)
+
+ verify(dragAnimator).cancelAnimator()
+ verify(dragCancelCallback).run()
+ verify(dragToDesktopStateListener).onTransitionInterrupted()
+ assertThat(defaultHandler.inProgress).isTrue()
+ // Doesn't finish start transition yet
+ verify(finishCallback, never()).onTransitionFinished(/* wct= */ anyOrNull())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_cancelAndEndNotYetRequested_finishesStartAfterAnimation() {
+ val finishCallback = mock<Transitions.TransitionFinishCallback>()
+ val task = createTask()
+ defaultHandler.onTaskResizeAnimationListener = mock()
+ val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+ mergeInterruptingTransition(mergeTarget = startTransition)
+ mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+
+ verify(finishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+ assertThat(defaultHandler.inProgress).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
fun mergeOtherTransition_endDragAlreadyMerged_doesNotInterruptStartDrag() {
val startDragFinishCallback = mock<Transitions.TransitionFinishCallback>()
val task = createTask()
@@ -795,6 +847,35 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
verify(dragAnimator, times(2)).startAnimation()
}
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun startCancelAnimation_otherTransitionInterruptingAfterCancelRequest_finishImmediately() {
+ val task1 = createTask()
+ val startTransition = startDrag(defaultHandler, task1)
+ val cancelTransition =
+ cancelDragToDesktopTransition(defaultHandler, CancelState.STANDARD_CANCEL)
+ mergeInterruptingTransition(mergeTarget = startTransition)
+ val cancelFinishCallback = mock<Transitions.TransitionFinishCallback>()
+ val startTransaction = mock<SurfaceControl.Transaction>()
+
+ val didAnimate =
+ defaultHandler.startAnimation(
+ transition = requireNotNull(cancelTransition),
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP,
+ draggedTask = task1,
+ ),
+ startTransaction = startTransaction,
+ finishTransaction = mock(),
+ finishCallback = cancelFinishCallback,
+ )
+
+ assertThat(didAnimate).isTrue()
+ verify(startTransaction).apply()
+ verify(cancelFinishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+ }
+
private fun mergeInterruptingTransition(mergeTarget: IBinder) {
defaultHandler.mergeAnimation(
transition = mock<IBinder>(),
@@ -942,7 +1023,12 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
)
.thenReturn(token)
- handler.startDragToDesktopTransition(task, dragAnimator, visualIndicator)
+ handler.startDragToDesktopTransition(
+ task,
+ dragAnimator,
+ visualIndicator,
+ dragCancelCallback,
+ )
return token
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index c7518d5914b4..3983bfbb2080 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -54,7 +54,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -111,7 +111,7 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
)
// Assert fadeIn, fadeOut, and animateIndicatorType were not called.
- verifyZeroInteractions(spyViewContainer)
+ verifyNoMoreInteractions(spyViewContainer)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 7560945856ec..dc973d0fda77 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -90,7 +90,7 @@ class SystemModalsTransitionHandlerTest : ShellTestCase() {
whenever(packageManager.getHomeActivities(ArrayList())).thenReturn(componentName)
desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
transitionHandler = createTransitionHandler()
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
}
private fun createTransitionHandler() =
@@ -200,10 +200,16 @@ class SystemModalsTransitionHandlerTest : ShellTestCase() {
.isTrue()
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
- whenever(packageManager.getPackageInfo(anyString(), eq(PackageManager.GET_PERMISSIONS)))
+ whenever(
+ packageManager.getPackageInfoAsUser(
+ anyString(),
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
+ )
+ )
.thenReturn(packageInfo)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 14f9ffc52a66..2bd9afcef1bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -107,7 +107,7 @@ public class PipAlphaAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index 72c466663a56..fa7ab9521dac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -24,7 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -117,7 +117,7 @@ public class PipEnterAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
// Check corner and shadow radii were set
verify(mMockAnimateTransaction, atLeastOnce())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index b816f0ef041e..97133bedfa2d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -21,7 +21,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -143,7 +143,7 @@ public class PipExpandAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 23fbad05ec99..c99ca6dd7065 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
@@ -118,7 +118,7 @@ public class PipResizeAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 5029371c3419..b6894fd0a9fa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -30,7 +30,7 @@ import static org.mockito.kotlin.MatchersKt.eq;
import static org.mockito.kotlin.VerificationKt.clearInvocations;
import static org.mockito.kotlin.VerificationKt.times;
import static org.mockito.kotlin.VerificationKt.verify;
-import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+import static org.mockito.kotlin.VerificationKt.verifyNoMoreInteractions;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -176,7 +176,7 @@ public class PipTaskListenerTest {
mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -193,7 +193,7 @@ public class PipTaskListenerTest {
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -245,7 +245,7 @@ public class PipTaskListenerTest {
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -262,7 +262,7 @@ public class PipTaskListenerTest {
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -319,7 +319,7 @@ public class PipTaskListenerTest {
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
extras);
- verifyZeroInteractions(mMockPipScheduler);
+ verifyNoMoreInteractions(mMockPipScheduler);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
index 82cdfd52d2db..51de50da6921 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Flags;
@@ -82,7 +82,7 @@ public class PipUiStateChangeControllerTests {
mPipUiStateChangeController.onPipTransitionStateChanged(
PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
- verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+ verifyNoMoreInteractions(mPictureInPictureUiStateConsumer);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index 5ac680048a7e..12785c03aa9f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -42,6 +42,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -87,7 +88,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -101,7 +102,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -115,7 +116,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptCachedPermissionCheckIsUsed() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -123,6 +124,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -131,10 +133,26 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
- verify(packageManager, times(1)).getPackageInfo(
+ assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ userId = 0
+ }))
+ verify(packageManager, times(1)).getPackageInfoAsUser(
+ eq("com.test.dummypackage"),
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(10)
+ )
+ verify(packageManager, times(1)).getPackageInfoAsUser(
eq("com.test.dummypackage"),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(0)
)
}
@@ -284,13 +302,14 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
}
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
whenever(
- packageManager.getPackageInfo(
+ packageManager.getPackageInfoAsUser(
anyString(),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
)
).thenReturn(packageInfo)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3099b0f5cf66..a122c3820dcb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
@@ -44,6 +45,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -196,6 +198,73 @@ public class HomeTransitionObserverTest extends ShellTestCase {
}
@Test
+ @DisableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopFinished_flagDisabled_doesNotTriggerCallback()
+ throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+ verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+ }
+
+ @Test
+ @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true);
+
+ verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+ }
+
+ @Test
+ @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopFinished_triggersCallback() throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+ verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true);
+ }
+
+ @Test
@EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index 067dcec5d65d..b1f92411c5a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,6 +28,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -109,7 +110,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
onTaskOpening(task, taskSurface)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -165,7 +166,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
setLargeScreen(false)
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index d69509faf4ec..ad3426e82805 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -51,6 +51,7 @@ import android.view.SurfaceView
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
@@ -134,7 +135,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -149,12 +150,12 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
val taskSurface = SurfaceControl()
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
task.setWindowingMode(WINDOWING_MODE_FREEFORM)
task.setActivityType(ACTIVITY_TYPE_STANDARD)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
}
@@ -758,20 +759,6 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
}
@Test
- fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
- val toSplitScreenListenerCaptor = forClass(Function0::class.java)
- as ArgumentCaptor<Function0<Unit>>
- val decor = createOpenTaskDecoration(
- windowingMode = WINDOWING_MODE_MULTI_WINDOW,
- onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
- )
-
- toSplitScreenListenerCaptor.value.invoke()
-
- verify(decor).disposeStatusBarInputLayer()
- }
-
- @Test
fun testDecor_onClickToOpenBrowser_closeMenus() {
val openInBrowserListenerCaptor = forClass(Consumer::class.java)
as ArgumentCaptor<Consumer<Intent>>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index a1f40fdefee9..4c9c2f14d805 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -84,6 +84,7 @@ import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
import org.mockito.Mockito
@@ -125,6 +126,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockShellController = mock<ShellController>()
protected val testShellExecutor = TestShellExecutor()
protected val mockAppHeaderViewHolderFactory = mock<AppHeaderViewHolder.Factory>()
+ protected val mockAppHandleViewHolderFactory = mock<AppHandleViewHolder.Factory>()
protected val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
protected val mockShellCommandHandler = mock<ShellCommandHandler>()
protected val mockWindowManager = mock<IWindowManager>()
@@ -222,6 +224,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockInputMonitorFactory,
transactionFactory,
mockAppHeaderViewHolderFactory,
+ mockAppHandleViewHolderFactory,
mockRootTaskDisplayAreaOrganizer,
windowDecorByTaskIdSpy,
mockInteractionJankMonitor,
@@ -331,7 +334,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
@@ -353,12 +356,17 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
)
}
- protected fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ protected fun onTaskChanging(
+ task: RunningTaskInfo,
+ leash: SurfaceControl = SurfaceControl(),
+ changeMode: Int
+ ) {
desktopModeWindowDecorViewModel.onTaskChanging(
task,
leash,
StubTransaction(),
- StubTransaction()
+ StubTransaction(),
+ changeMode
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 878324937f1a..f37f2fb14bea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -115,6 +115,7 @@ import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
@@ -171,6 +172,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
+ private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
+ private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
+
@Mock
private DisplayController mMockDisplayController;
@@ -191,8 +195,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
@Mock
+ private AppHandleViewHolder.Factory mMockAppHandleViewHolderFactory;
+ @Mock
private AppHeaderViewHolder mMockAppHeaderViewHolder;
@Mock
+ private AppHandleViewHolder mMockAppHandleViewHolder;
+ @Mock
private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
@Mock
private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@@ -301,6 +309,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
when(mMockAppHeaderViewHolderFactory
.create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
+ when(mMockAppHandleViewHolderFactory
+ .create(any(), any(), any(), any(), any()))
+ .thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -421,7 +432,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
/* shouldIgnoreCornerRadius= */ true,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
}
@@ -623,7 +636,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- /* shouldExcludeCaptionFromAppBounds */ true);
+ /* shouldExcludeCaptionFromAppBounds */ true,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Force consuming flags are disabled.
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -658,7 +673,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
assertThat(
@@ -737,7 +754,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -765,7 +784,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -792,7 +813,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -819,7 +842,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -845,7 +870,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -871,7 +898,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -898,7 +927,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -917,7 +948,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -944,7 +977,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -971,7 +1006,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1002,6 +1039,65 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
assertThat(relayoutParams.mAsyncViewHost).isFalse();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ /* isMovingToBack= */ true);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ /* isRecentsTransitionRunning= */ true,
+ DEFAULT_IS_MOVING_TO_BACK);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
@Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1633,7 +1729,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
}
private DesktopModeWindowDecoration createWindowDecoration(
@@ -1676,9 +1774,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
- mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
- mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
+ mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 7341e098add5..360099777bde 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -40,11 +40,13 @@ import com.android.wm.shell.windowdecor.DragResizeInputListener.TaskResizeInputE
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import java.util.function.Supplier
+import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -63,6 +65,15 @@ class DragResizeInputListenerTest : ShellTestCase() {
private val testBgExecutor = TestShellExecutor()
private val mockWindowSession = mock<IWindowSession>()
private val mockInputEventReceiver = mock<TaskResizeInputEventReceiver>()
+ private val inputChannel = mock<InputChannel>()
+ private val sinkInputChannel = mock<InputChannel>()
+ private val decorationSurface = SurfaceControl.Builder().setName("decoration surface").build()
+ private val createdSurfaces = ArrayList<SurfaceControl>()
+
+ @After
+ fun tearDown() {
+ decorationSurface.release()
+ }
@Test
fun testGrantInputChannelOffMainThread() {
@@ -73,6 +84,35 @@ class DragResizeInputListenerTest : ShellTestCase() {
}
@Test
+ fun testGrantInputChannelAfterDecorSurfaceReleased() {
+ // Keep tracking the underlying surface that the decorationSurface points to.
+ val forVerification = SurfaceControl(decorationSurface, "forVerification")
+ try {
+ create()
+ decorationSurface.release()
+ testBgExecutor.flushAll()
+
+ verify(mockWindowSession)
+ .grantInputChannel(
+ anyInt(),
+ argThat<SurfaceControl> { isValid && isSameSurface(forVerification) },
+ any(),
+ anyOrNull(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyOrNull(),
+ any(),
+ any(),
+ any(),
+ )
+ } finally {
+ forVerification.release()
+ }
+ }
+
+ @Test
fun testInitializationCallback_waitsForBgSetup() {
val inputListener = create()
@@ -143,6 +183,40 @@ class DragResizeInputListenerTest : ShellTestCase() {
verify(mockWindowSession).remove(inputListener.mSinkClientToken)
}
+ @Test
+ fun testClose_afterBgSetup_disposesOfInputChannels() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ verify(inputChannel).dispose()
+ verify(sinkInputChannel).dispose()
+ }
+
+ @Test
+ fun testClose_beforeBgSetup_releaseSurfaces() {
+ val inputListener = create()
+ inputListener.close()
+ testBgExecutor.flushAll()
+ testMainExecutor.flushAll()
+
+ assertThat(createdSurfaces).hasSize(1)
+ assertThat(createdSurfaces[0].isValid).isFalse()
+ }
+
+ @Test
+ fun testClose_afterBgSetup_releaseSurfaces() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ testBgExecutor.flushAll()
+
+ assertThat(createdSurfaces).hasSize(2)
+ assertThat(createdSurfaces[0].isValid).isFalse()
+ assertThat(createdSurfaces[1].isValid).isFalse()
+ }
+
private fun verifyNoInputChannelGrantRequests() {
verify(mockWindowSession, never())
.grantInputChannel(
@@ -172,12 +246,26 @@ class DragResizeInputListenerTest : ShellTestCase() {
TestHandler(Looper.getMainLooper()),
mock<Choreographer>(),
Display.DEFAULT_DISPLAY,
- mock<SurfaceControl>(),
+ decorationSurface,
mock<DragPositioningCallback>(),
- { SurfaceControl.Builder() },
- { StubTransaction() },
+ {
+ object : SurfaceControl.Builder() {
+ override fun build(): SurfaceControl {
+ return super.build().also { createdSurfaces.add(it) }
+ }
+ }
+ },
+ {
+ object : StubTransaction() {
+ override fun remove(sc: SurfaceControl): SurfaceControl.Transaction {
+ return super.remove(sc).also { sc.release() }
+ }
+ }
+ },
mock<DisplayController>(),
mock<DesktopModeEventLogger>(),
+ inputChannel,
+ sinkInputChannel,
)
private class TestInitializationCallback : Runnable {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f984f6db13fc..2e46f6312d03 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -98,8 +98,6 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var handleMenu: HandleMenu
- private val menuWidthWithElevation = MENU_WIDTH + MENU_PILL_ELEVATION
-
@Before
fun setUp() {
val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer(
@@ -126,7 +124,6 @@ class HandleMenuTest : ShellTestCase() {
addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
- addOverride(R.dimen.desktop_mode_handle_menu_pill_elevation, MENU_PILL_ELEVATION)
addOverride(
R.dimen.desktop_mode_handle_menu_pill_spacing_margin, MENU_PILL_SPACING_MARGIN)
}
@@ -141,7 +138,7 @@ class HandleMenuTest : ShellTestCase() {
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of display.
- val expected = Point(DISPLAY_BOUNDS.centerX() - menuWidthWithElevation / 2, MENU_TOP_MARGIN)
+ val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
}
@@ -165,7 +162,7 @@ class HandleMenuTest : ShellTestCase() {
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split left task.
val expected = Point(
- SPLIT_LEFT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -180,7 +177,7 @@ class HandleMenuTest : ShellTestCase() {
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split right task.
val expected = Point(
- SPLIT_RIGHT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -323,7 +320,6 @@ class HandleMenuTest : ShellTestCase() {
private const val MENU_HEIGHT = 400
private const val MENU_TOP_MARGIN = 10
private const val MENU_START_MARGIN = 20
- private const val MENU_PILL_ELEVATION = 2
private const val MENU_PILL_SPACING_MARGIN = 4
private const val HANDLE_WIDTH = 80
private const val APP_NAME = "Test App"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index a6b077037b86..0798613ed632 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -559,6 +559,17 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ fun testClose() = runOnUiThread {
+ verify(mockDisplayController, times(1))
+ .addDisplayWindowListener(eq(taskPositioner))
+
+ taskPositioner.close()
+
+ verify(mockDisplayController, times(1))
+ .removeDisplayWindowListener(eq(taskPositioner))
+ }
+
+ @Test
fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
Assert.assertFalse(taskPositioner.isResizingOrAnimating)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index fa3d3e4016e9..011c8f0ae17e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -52,7 +52,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@@ -216,7 +216,7 @@ class ResizeVeilTest : ShellTestCase() {
veil.hideVeil()
- verifyZeroInteractions(mockTransaction)
+ verifyNoMoreInteractions(mockTransaction)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index cda343f3538b..2e95a979220c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -826,6 +826,18 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Test
+ public void testClose_withTaskDragResizerSet_callResizerClose() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ final TaskDragResizer taskDragResizer = mock(TaskDragResizer.class);
+ windowDecor.setTaskDragResizer(taskDragResizer);
+
+ windowDecor.close();
+
+ verify(taskDragResizer).close();
+ }
+
+ @Test
public void testRelayout_captionFrameChanged_insetsReapplied() {
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController)
@@ -1232,6 +1244,11 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Override
+ int getCaptionViewId() {
+ return R.id.caption;
+ }
+
+ @Override
TestView inflateLayout(Context context, int layoutResId) {
if (layoutResId == R.layout.caption_layout) {
return mMockView;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index c8ccac35d4c4..714d06211044 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -54,7 +54,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -125,7 +125,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getName(task)
- verifyZeroInteractions(
+ verifyNoMoreInteractions(
mockPackageManager,
mockIconProvider,
mockHeaderIconFactory,
@@ -165,7 +165,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getHeaderIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
}
@Test
@@ -187,7 +187,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getVeilIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
}
@Test
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index f8eb41826c6f..109d9e83d444 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -186,3 +186,21 @@ flag {
bug: "398254728"
is_fixed_read_only: true
}
+
+flag {
+ name: "limit_fused_gps"
+ namespace: "location"
+ description: "Limits when GPS can be used for fused location requests"
+ bug: "401885179"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "gnss_assistance_interface_jni"
+ namespace: "location"
+ description: "Flag for GNSS assistance interface JNI"
+ bug: "209078566"
+}
+
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b57476f4341f..6e0821f9f89b 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -183,7 +183,17 @@ public class MediaRouter {
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- mDisplayService.registerDisplayListener(this, mHandler);
+ if (com.android.server.display.feature.flags.Flags
+ .displayListenerPerformanceImprovements()
+ && com.android.server.display.feature.flags.Flags
+ .delayImplicitRrRegistrationUntilRrAccessed()) {
+ mDisplayService.registerDisplayListener(this, mHandler,
+ DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+ | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+ | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+ } else {
+ mDisplayService.registerDisplayListener(this, mHandler);
+ }
AudioRoutesInfo newAudioRoutes = null;
try {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index e39a0aa8717e..48e2f4e15238 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -242,3 +242,13 @@ flag {
description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
bug: "293743975"
}
+
+flag {
+ name: "fix_output_media_item_list_index_out_of_bounds_exception"
+ namespace: "media_better_together"
+ description: "Fixes a bug of causing IndexOutOfBoundsException when building media item list."
+ bug: "398246089"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/media/java/android/media/quality/Android.bp b/media/java/android/media/quality/Android.bp
index 080d5266ccb7..f620144e2880 100644
--- a/media/java/android/media/quality/Android.bp
+++ b/media/java/android/media/quality/Android.bp
@@ -15,6 +15,30 @@ filegroup {
path: "aidl",
}
+cc_library_headers {
+ name: "media_quality_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+ name: "libmedia_quality_include",
+
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+
+ srcs: [
+ ":framework-media-quality-sources-aidl",
+ ],
+}
+
aidl_interface {
name: "media_quality_aidl_interface",
unstable: true,
@@ -24,7 +48,8 @@ aidl_interface {
enabled: true,
},
cpp: {
- enabled: false,
+ additional_shared_libraries: ["libmedia_quality_include"],
+ enabled: true,
},
ndk: {
enabled: false,
diff --git a/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
index 2851306f6e4d..d2cf140632ab 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable ActiveProcessingPicture; \ No newline at end of file
+parcelable ActiveProcessingPicture cpp_header "quality/MediaQualityManager.h"; \ No newline at end of file
diff --git a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
index 174cd461e846..d53860fdf9ad 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable AmbientBacklightEvent;
+parcelable AmbientBacklightEvent cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
index b95a474fbf90..a935b49b5d23 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable AmbientBacklightMetadata; \ No newline at end of file
+parcelable AmbientBacklightMetadata cpp_header "quality/MediaQualityManager.h"; \ No newline at end of file
diff --git a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
index e2cdd03194cd..051aef80b948 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable AmbientBacklightSettings;
+parcelable AmbientBacklightSettings cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
index eb2ac97916f3..ea848576e026 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable ParameterCapability;
+parcelable ParameterCapability cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
index 41d018b12f33..b0fe3f5538f4 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable PictureProfile;
+parcelable PictureProfile cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
index 5d14631dbb73..0582938b6ea7 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable PictureProfileHandle;
+parcelable PictureProfileHandle cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
index e79fcaac97be..d93231fbf7e0 100644
--- a/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
@@ -16,4 +16,4 @@
package android.media.quality;
-parcelable SoundProfile;
+parcelable SoundProfile cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/include/quality/MediaQualityManager.h b/media/java/android/media/quality/include/quality/MediaQualityManager.h
new file mode 100644
index 000000000000..8c31667077c3
--- /dev/null
+++ b/media/java/android/media/quality/include/quality/MediaQualityManager.h
@@ -0,0 +1,127 @@
+#ifndef ANDROID_MEDIA_QUALITY_MANAGER_H
+#define ANDROID_MEDIA_QUALITY_MANAGER_H
+
+
+namespace android {
+namespace media {
+namespace quality {
+
+// TODO: implement writeToParcel and readFromParcel
+
+class PictureProfileHandle : public Parcelable {
+ public:
+ PictureProfileHandle() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class SoundProfile : public Parcelable {
+ public:
+ SoundProfile() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class PictureProfile : public Parcelable {
+ public:
+ PictureProfile() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class ActiveProcessingPicture : public Parcelable {
+ public:
+ ActiveProcessingPicture() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class AmbientBacklightEvent : public Parcelable {
+ public:
+ AmbientBacklightEvent() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class AmbientBacklightMetadata : public Parcelable {
+ public:
+ AmbientBacklightMetadata() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class AmbientBacklightSettings : public Parcelable {
+ public:
+ AmbientBacklightSettings() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+class ParameterCapability : public Parcelable {
+ public:
+ ParameterCapability() {}
+ status_t writeToParcel(android::Parcel*) const override {
+ return 0;
+ }
+ status_t readFromParcel(const android::Parcel*) override {
+ return 0;
+ }
+ std::string toString() const {
+ return "";
+ }
+};
+
+} // namespace quality
+} // namespace media
+} // namespace android
+
+#endif
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 6089f4291f3e..f65c7efa8ca7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.companion.virtual.VirtualDeviceManager;
@@ -56,7 +56,7 @@ public class AudioManagerUnitTest {
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect no interactions with VDM when running on default device.
- verifyZeroInteractions(mockVdm);
+ verifyNoMoreInteractions(mockVdm);
}
@Test
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 65264d30f04f..006b86a63f60 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -16,9 +16,9 @@
package com.android.mediaframeworktest.unit;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index 6229434d1d86..7c54ad2f231c 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -16,7 +16,7 @@
package com.android.carrierdefaultapp;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index b2c1e604db7e..964268e4ad14 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -65,6 +65,7 @@ import android.graphics.drawable.Icon;
import android.net.MacAddress;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.text.Spanned;
@@ -621,8 +622,10 @@ public class CompanionAssociationActivity extends FragmentActivity implements
Slog.w(TAG, "Already selected.");
return;
}
- // Notify the adapter to highlight the selected item.
- mDeviceAdapter.setSelectedPosition(position);
+ // Delay highlighting the selected item by posting to the main thread.
+ // This helps avoid flicker in the user consent dialog after device selection.
+ new Handler(
+ Looper.getMainLooper()).post(() -> mDeviceAdapter.setSelectedPosition(position));
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 8e52a00fe545..a0e008c9437f 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -19,6 +19,7 @@ package com.android.location.fused;
import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
import static android.location.LocationRequest.QUALITY_LOW_POWER;
import static android.location.provider.ProviderProperties.ACCURACY_FINE;
import static android.location.provider.ProviderProperties.POWER_USAGE_LOW;
@@ -34,6 +35,7 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
+import android.location.flags.Flags;
import android.location.provider.LocationProviderBase;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -61,6 +63,9 @@ public class FusedLocationProvider extends LocationProviderBase {
.build();
private static final long MAX_LOCATION_COMPARISON_NS = 11 * 1000000000L; // 11 seconds
+ // Maximum request interval at which we will activate GPS (because GPS sometimes consumes
+ // excessive power with large intervals).
+ private static final long MAX_GPS_INTERVAL_MS = 5 * 1000; // 5 seconds
private final Object mLock = new Object();
@@ -165,8 +170,13 @@ public class FusedLocationProvider extends LocationProviderBase {
mNlpPresent = mLocationManager.hasProvider(NETWORK_PROVIDER);
}
+ boolean requestAllowsGps =
+ Flags.limitFusedGps()
+ ? mRequest.getQuality() == QUALITY_HIGH_ACCURACY
+ && mRequest.getIntervalMillis() <= MAX_GPS_INTERVAL_MS
+ : !mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER;
long gpsInterval =
- mGpsPresent && (!mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER)
+ mGpsPresent && requestAllowsGps
? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
long networkInterval = mNlpPresent ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 50331014f926..716845c5b985 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -23,6 +23,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
@@ -120,6 +142,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 472ffa9289a7..6dec2f999630 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.datastore
import android.content.SharedPreferences
+import android.util.Log
/** Interface of key-value store. */
interface KeyValueStore : KeyedObservable<String> {
@@ -80,6 +81,27 @@ interface KeyValueStore : KeyedObservable<String> {
fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
}
+/** Delegation of [KeyValueStore]. */
+interface KeyValueStoreDelegate : KeyValueStore, KeyedObservableDelegate<String> {
+
+ /** [KeyValueStore] to delegate. */
+ val keyValueStoreDelegate: KeyValueStore
+
+ override val keyedObservableDelegate
+ get() = keyValueStoreDelegate
+
+ override fun contains(key: String) = keyValueStoreDelegate.contains(key)
+
+ override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getDefaultValue(key, valueType)
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getValue(key, valueType) ?: getDefaultValue(key, valueType)
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+ keyValueStoreDelegate.setValue(key, valueType, value)
+}
+
/** [SharedPreferences] based [KeyValueStore]. */
interface SharedPreferencesKeyValueStore : KeyValueStore {
@@ -103,11 +125,11 @@ interface SharedPreferencesKeyValueStore : KeyValueStore {
@Suppress("UNCHECKED_CAST")
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ val edit = sharedPreferences.edit()
if (value == null) {
- sharedPreferences.edit().remove(key).apply()
+ edit.remove(key).apply()
return
}
- val edit = sharedPreferences.edit()
when (valueType) {
Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
Float::class.javaObjectType -> edit.putFloat(key, value as Float)
@@ -115,7 +137,7 @@ interface SharedPreferencesKeyValueStore : KeyValueStore {
Long::class.javaObjectType -> edit.putLong(key, value as Long)
String::class.javaObjectType -> edit.putString(key, value as String)
Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
- else -> {}
+ else -> Log.e(LOG_TAG, "Unsupported $valueType for $key: $value")
}
edit.apply()
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 07b1c9e3385e..ff58bf7b8728 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -116,8 +116,28 @@ interface KeyedObservable<K> {
}
/** Delegation of [KeyedObservable]. */
-open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
- KeyedObservable<K> by delegate
+interface KeyedObservableDelegate<K> : KeyedObservable<K> {
+
+ /** [KeyedObservable] to delegate. */
+ val keyedObservableDelegate: KeyedObservable<K>
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(observer, executor)
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(key, observer, executor)
+
+ override fun removeObserver(observer: KeyedObserver<K?>): Boolean =
+ keyedObservableDelegate.removeObserver(observer)
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean =
+ keyedObservableDelegate.removeObserver(key, observer)
+
+ override fun notifyChange(reason: Int): Unit = keyedObservableDelegate.notifyChange(reason)
+
+ override fun notifyChange(key: K, reason: Int): Unit =
+ keyedObservableDelegate.notifyChange(key, reason)
+}
/** A thread safe implementation of [KeyedObservable]. */
open class KeyedDataObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 000000000000..fdde3d3f5669
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+ /** Listener for changes in device-state based auto rotate setting. */
+ interface DeviceStateAutoRotateSettingListener {
+ /** Called whenever the setting has changed. */
+ fun onSettingsChanged()
+ }
+
+ /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /**
+ * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+ */
+ fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+ /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+ fun getRotationLockSetting(deviceState: Int): Int
+
+ /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+ fun isRotationLocked(deviceState: Int): Boolean
+
+ /** Returns true if the auto-rotate setting value for all device states is OFF. */
+ fun isRotationLockedForAllStates(): Boolean
+
+ /** Returns a list of device states and their respective auto rotate setting availability. */
+ fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+ /** Returns the device state associated with this object. */
+ val deviceState: Int,
+ /** Returns whether there is an auto-rotation setting for this device state. */
+ val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 000000000000..0b6c6e238956
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+ context: Context,
+ backgroundExecutor: Executor,
+ private val secureSettings: SecureSettings,
+ private val mainHandler: Handler,
+ private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+ private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+ mutableListOf()
+ private val fallbackPostureMap = SparseIntArray()
+ private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+ private val autoRotateSettingValue: String
+ get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+ init {
+ loadAutoRotateDeviceStates(context)
+ val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) = notifyListeners()
+ }
+ backgroundExecutor.execute {
+ secureSettings.registerContentObserver(
+ DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+ )
+ }
+ }
+
+ override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ settingListeners.add(settingListener)
+ }
+
+ override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ if (!settingListeners.remove(settingListener)) {
+ Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+ }
+ }
+
+ override fun getRotationLockSetting(deviceState: Int): Int {
+ val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+ val serializedSetting = autoRotateSettingValue
+ val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+ // If the setting is ignored for this posture, check the fallback posture.
+ if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ val fallbackPosture =
+ fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+ }
+
+ return autoRotateSetting
+ }
+
+ override fun isRotationLocked(deviceState: Int) =
+ getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+ override fun isRotationLockedForAllStates(): Boolean =
+ convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+ value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+ }
+
+ override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+ override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+ // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+ throw UnsupportedOperationException("API updateSetting is not implemented yet")
+ }
+
+ private fun notifyListeners() =
+ settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+ private fun loadAutoRotateDeviceStates(context: Context) {
+ val perDeviceStateAutoRotateDefaults =
+ context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ for (entry in perDeviceStateAutoRotateDefaults) {
+ entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+ if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+ fallbackPostureMap.put(posture, fallbackPosture)
+ }
+ settableDeviceState.add(
+ SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ )
+ }
+ }
+ }
+
+ private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+ if (serializedSetting.isEmpty()) return emptyMap()
+ return try {
+ serializedSetting
+ .split(SEPARATOR_REGEX)
+ .hasEvenSize()
+ .chunked(2)
+ .mapNotNull(::parsePostureSettingPair)
+ .toMap()
+ } catch (e: Exception) {
+ Log.w(
+ TAG,
+ "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+ )
+ return emptyMap()
+ }
+ }
+
+ private fun List<String>.hasEvenSize(): List<String> {
+ if (this.size % 2 != 0) {
+ throw IllegalStateException("Odd number of elements in the list")
+ }
+ return this
+ }
+
+ private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+ return settingPair.let { (keyStr, valueStr) ->
+ val key = keyStr.toIntOrNull()
+ val value = valueStr.toIntOrNull()
+ if (key != null && value != null && value in 0..2) {
+ key to value
+ } else {
+ Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+ null // Invalid pair, skip it
+ }
+ }
+ }
+
+ private fun extractSettingForDevicePosture(
+ devicePosture: Int,
+ serializedSetting: String
+ ): Int =
+ convertSerializedSettingToMap(serializedSetting)[devicePosture]
+ ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+ private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+ val values = split(SEPARATOR_REGEX)
+ if (values.size !in 2..3) { // It should contain 2 or 3 values.
+ Log.w(TAG, "Invalid number of values in entry: '$this'")
+ return null
+ }
+ return try {
+ val posture = values[0].toInt()
+ val rotationLockSetting = values[1].toInt()
+ val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+ Triple(posture, rotationLockSetting, fallbackPosture)
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+ null
+ }
+ }
+
+ companion object {
+ private const val TAG = "DeviceStateAutoRotate"
+ private const val SEPARATOR_REGEX = ":"
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 000000000000..4d1d29242832
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+ @JvmStatic
+ fun isDeviceStateRotationLockEnabled(context: Context) =
+ context.resources
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f6905e4f0..deeba574f2ad 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -43,7 +45,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
/**
@@ -58,7 +59,7 @@ public final class DeviceStateRotationLockSettingsManager {
private static DeviceStateRotationLockSettingsManager sSingleton;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
private final SecureSettings mSecureSettings;
private final PosturesHelper mPosturesHelper;
private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@ public final class DeviceStateRotationLockSettingsManager {
}
/**
- * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+ * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+ public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
/**
- * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+ * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
public void unregisterListener(
- DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
- if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+ DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
@@ -379,56 +380,8 @@ public final class DeviceStateRotationLockSettingsManager {
}
private void notifyListeners() {
- for (DeviceStateRotationLockSettingsListener r : mListeners) {
+ for (DeviceStateAutoRotateSettingListener r : mListeners) {
r.onSettingsChanged();
}
}
-
- /** Listener for changes in device-state based rotation lock settings */
- public interface DeviceStateRotationLockSettingsListener {
- /** Called whenever the settings have changed. */
- void onSettingsChanged();
- }
-
- /** Represents a device state and whether it has an auto-rotation setting. */
- public static class SettableDeviceState {
- private final int mDeviceState;
- private final boolean mIsSettable;
-
- SettableDeviceState(int deviceState, boolean isSettable) {
- mDeviceState = deviceState;
- mIsSettable = isSettable;
- }
-
- /** Returns the device state associated with this object. */
- public int getDeviceState() {
- return mDeviceState;
- }
-
- /** Returns whether there is an auto-rotation setting for this device state. */
- public boolean isSettable() {
- return mIsSettable;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SettableDeviceState)) return false;
- SettableDeviceState that = (SettableDeviceState) o;
- return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceState, mIsSettable);
- }
-
- @Override
- public String toString() {
- return "SettableDeviceState{"
- + "mDeviceState=" + mDeviceState
- + ", mIsSettable=" + mIsSettable
- + '}';
- }
- }
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 10528739b2b0..ea40e148aed6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@ package com.android.settingslib.devicestate;
import android.database.ContentObserver;
/** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
void putStringForUser(String name, String value, int userHandle);
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 13541b1ebc9a..009d265833b4 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -58,6 +58,7 @@ import com.android.settingslib.metadata.PreferenceTitleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel.Companion.HIGH_SENSITIVITY
import com.android.settingslib.metadata.SensitivityLevel.Companion.UNKNOWN_SENSITIVITY
+import com.android.settingslib.metadata.getPreferenceIcon
import com.android.settingslib.preference.PreferenceScreenFactory
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/strings.xml b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
index 3a8aaf8b5092..03da0dc41d47 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
@@ -20,8 +20,6 @@
<string name="settingslib_action_label_resume">resume</string>
<!-- Label for an accessibility action that stops an animation [CHAR LIMIT=30] -->
<string name="settingslib_action_label_pause">pause</string>
- <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] -->
- <string name="settingslib_state_animation_playing">Animation playing</string>
- <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] -->
- <string name="settingslib_state_animation_paused">Animation paused</string>
+ <!-- Default content description attached to the illustration if there is no content description. [CHAR LIMIT=NONE] -->
+ <string name="settingslib_illustration_content_description">Animation</string>
</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index bf739620bc99..777607010b3a 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -474,6 +474,10 @@ public class IllustrationPreference extends Preference implements GroupSectionDi
if (mIsAnimatable) {
// TODO(b/397340540): list out pages having illustration without a content description.
if (TextUtils.isEmpty(mContentDescription)) {
+ // Default content description will be attached if there's no content description.
+ illustrationView.setContentDescription(
+ getContext().getString(
+ R.string.settingslib_illustration_content_description));
Log.w(TAG, "Illustration should have a content description. preference key = "
+ getKey());
}
@@ -493,8 +497,6 @@ public class IllustrationPreference extends Preference implements GroupSectionDi
}
private void updateAccessibilityAction(ViewGroup container) {
- // Setting the state of animation
- container.setStateDescription(getStateDescriptionForAnimation());
container.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -515,14 +517,6 @@ public class IllustrationPreference extends Preference implements GroupSectionDi
}
}
- private String getStateDescriptionForAnimation() {
- if (mIsAnimationPaused) {
- return getContext().getString(R.string.settingslib_state_animation_paused);
- } else {
- return getContext().getString(R.string.settingslib_state_animation_playing);
- }
- }
-
private static void startLottieAnimationWith(LottieAnimationView illustrationView,
Uri imageUri) {
final InputStream inputStream =
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 7bd4b3f771ab..9d4c5c2735fc 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -113,8 +113,6 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
mSwitch.setOnCheckedChangeListener(this);
}
- setChecked(mSwitch.isChecked());
-
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs,
androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
@@ -130,8 +128,6 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
}
a.recycle();
}
-
- setBackground(mSwitch.isChecked());
}
@Override
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
index 7f2a61081fbb..fcca82330d10 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -137,44 +137,6 @@ interface PreferenceMetadata {
/** Returns preference intent. */
fun intent(context: Context): Intent? = null
-
- /**
- * Returns the preference title.
- *
- * Implement [PreferenceTitleProvider] interface if title content is generated dynamically.
- */
- fun getPreferenceTitle(context: Context): CharSequence? =
- when {
- title != 0 -> context.getText(title)
- this is PreferenceTitleProvider -> getTitle(context)
- else -> null
- }
-
- /**
- * Returns the preference summary.
- *
- * Implement [PreferenceSummaryProvider] interface if summary content is generated dynamically
- * (e.g. summary is provided per preference value).
- */
- fun getPreferenceSummary(context: Context): CharSequence? =
- when {
- summary != 0 -> context.getText(summary)
- this is PreferenceSummaryProvider -> getSummary(context)
- else -> null
- }
-
- /**
- * Returns the preference icon.
- *
- * Implement [PreferenceIconProvider] interface if icon is provided dynamically (e.g. icon is
- * provided based on flag value).
- */
- fun getPreferenceIcon(context: Context): Int =
- when {
- icon != 0 -> icon
- this is PreferenceIconProvider -> getIcon(context)
- else -> 0
- }
}
/** Metadata of preference group. */
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt
new file mode 100644
index 000000000000..6d580fb47160
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.metadata
+
+import android.content.Context
+
+/** Returns the preference screen title. */
+fun PreferenceScreenMetadata.getPreferenceScreenTitle(context: Context): CharSequence? =
+ when {
+ screenTitle != 0 -> context.getString(screenTitle)
+ else -> getScreenTitle(context) ?: (this as? PreferenceTitleProvider)?.getTitle(context)
+ }
+
+/** Returns the preference title. */
+fun PreferenceMetadata.getPreferenceTitle(context: Context): CharSequence? =
+ when {
+ title != 0 -> context.getText(title)
+ this is PreferenceTitleProvider -> getTitle(context)
+ else -> null
+ }
+
+/** Returns the preference summary. */
+fun PreferenceMetadata.getPreferenceSummary(context: Context): CharSequence? =
+ when {
+ summary != 0 -> context.getText(summary)
+ this is PreferenceSummaryProvider -> getSummary(context)
+ else -> null
+ }
+
+/** Returns the preference icon. */
+fun PreferenceMetadata.getPreferenceIcon(context: Context): Int =
+ when {
+ icon != 0 -> icon
+ this is PreferenceIconProvider -> getIcon(context)
+ else -> 0
+ }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 59141c99e587..8896af47a1c2 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -25,10 +25,16 @@ import androidx.preference.PreferenceScreen
import androidx.preference.SeekBarPreference
import com.android.settingslib.metadata.DiscreteIntValue
import com.android.settingslib.metadata.DiscreteValue
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.IntRangeValuePreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.getPreferenceIcon
+import com.android.settingslib.metadata.getPreferenceScreenTitle
+import com.android.settingslib.metadata.getPreferenceSummary
+import com.android.settingslib.metadata.getPreferenceTitle
/** Binding of preference widget and preference metadata. */
interface PreferenceBinding {
@@ -72,9 +78,22 @@ interface PreferenceBinding {
preference.icon = null
}
val isPreferenceScreen = preference is PreferenceScreen
+ val screenMetadata = this as? PreferenceScreenMetadata
+ // extras
preference.peekExtras()?.clear()
extras(context)?.let { preference.extras.putAll(it) }
- preference.title = getPreferenceTitle(context)
+ if (!isPreferenceScreen && screenMetadata != null) {
+ val extras = preference.extras
+ // Pass the preference key to fragment, so that the fragment could find associated
+ // preference screen registered in PreferenceScreenRegistry
+ extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
+ }
+ preference.title =
+ when {
+ isPreferenceScreen -> screenMetadata?.getPreferenceScreenTitle(context)
+ else -> getPreferenceTitle(context)
+ }
if (!isPreferenceScreen) {
preference.summary = getPreferenceSummary(context)
}
@@ -82,12 +101,12 @@ interface PreferenceBinding {
preference.isVisible =
(this as? PreferenceAvailabilityProvider)?.isAvailable(context) != false
preference.isPersistent = isPersistent(context)
- // PreferenceRegistry will notify dependency change, so we do not need to set
+ // PreferenceScreenBindingHelper will notify dependency change, so we do not need to set
// dependency here. This simplifies dependency management and avoid the
// IllegalStateException when call Preference.setDependency
preference.dependency = null
if (!isPreferenceScreen) { // avoid recursive loop when build graph
- preference.fragment = (this as? PreferenceScreenCreator)?.fragmentClass()?.name
+ preference.fragment = screenMetadata?.fragmentClass()?.name
preference.intent = intent(context)
}
if (preference is DialogPreference) {
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 6287fda86a73..33b614e19bbe 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -60,7 +60,6 @@ open class DefaultPreferenceBindingFactory : PreferenceBindingFactory {
?: when (metadata) {
is SwitchPreference -> SwitchPreferenceBinding.INSTANCE
is PreferenceCategory -> PreferenceCategoryBinding.INSTANCE
- is PreferenceScreenCreator -> PreferenceScreenBinding.INSTANCE
is MainSwitchPreference -> MainSwitchPreferenceBinding.INSTANCE
else -> DefaultPreferenceBinding
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 44c93c77e33b..71c46fa76aed 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -19,45 +19,11 @@ package com.android.settingslib.preference
import android.content.Context
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
-import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
-import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
-import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceScreenMetadata
-import com.android.settingslib.metadata.PreferenceTitleProvider
import com.android.settingslib.widget.MainSwitchPreference
-/** Binding of preference group associated with [PreferenceCategory]. */
-interface PreferenceScreenBinding : PreferenceBinding {
-
- override fun bind(preference: Preference, metadata: PreferenceMetadata) {
- super.bind(preference, metadata)
- val context = preference.context
- val screenMetadata = metadata as PreferenceScreenMetadata
- val extras = preference.extras
- // Pass the preference key to fragment, so that the fragment could find associated
- // preference screen registered in PreferenceScreenRegistry
- extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
- screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
- if (preference is PreferenceScreen) {
- val screenTitle = screenMetadata.screenTitle
- preference.title =
- if (screenTitle != 0) {
- context.getString(screenTitle)
- } else {
- screenMetadata.getScreenTitle(context)
- ?: (screenMetadata as? PreferenceTitleProvider)?.getTitle(context)
- }
- }
- }
-
- companion object {
- @JvmStatic val INSTANCE = object : PreferenceScreenBinding {}
- }
-}
-
/** Binding of preference category associated with [PreferenceCategory]. */
interface PreferenceCategoryBinding : PreferenceBinding {
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
index 686c1488fb62..112a69bb47ec 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -262,4 +262,30 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
+
+ <style name="SettingsLibEntityHeaderContent">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_centerHorizontal">true</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:gravity">center_horizontal</item>
+ </style>
+
+ <style name="SettingsLibEntityHeaderIcon">
+ <item name="android:layout_width">@dimen/settingslib_expressive_space_large3</item>
+ <item name="android:layout_height">@dimen/settingslib_expressive_space_large3</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="SettingsLibEntityHeaderTitle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginTop">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:singleLine">false</item>
+ <item name="android:gravity">center</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index fe8e8b6f1d46..6d02c5d48715 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -16,6 +16,8 @@
package com.android.settingslib.widget;
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -46,6 +48,9 @@ import com.google.android.material.slider.Slider;
*/
public class SliderPreference extends Preference {
private static final String TAG = "SliderPreference";
+ public static final int HAPTIC_FEEDBACK_MODE_NONE = 0;
+ public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1;
+ public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2;
private final int mTextStartId;
private final int mTextEndId;
@@ -71,6 +76,8 @@ public class SliderPreference extends Preference {
private int mMin;
private int mMax;
private int mSliderIncrement;
+ private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE;
+ private boolean mTickVisible = false;
private boolean mAdjustable;
private boolean mTrackingTouch;
private CharSequence mSliderContentDescription;
@@ -265,6 +272,7 @@ public class SliderPreference extends Preference {
}
if (mSliderIncrement != 0) {
mSlider.setStepSize(mSliderIncrement);
+ mSlider.setTickVisible(mTickVisible);
} else {
mSliderIncrement = (int) (mSlider.getStepSize());
}
@@ -442,6 +450,29 @@ public class SliderPreference extends Preference {
}
/**
+ * Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback
+ * as the {@link Slider} value is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic
+ * feedback as the {@link Slider} value is equal to the min/max value.
+ *
+ * @param hapticFeedbackMode The haptic feedback mode.
+ */
+ public void setHapticFeedbackMode(int hapticFeedbackMode) {
+ mHapticFeedbackMode = hapticFeedbackMode;
+ }
+
+ /**
+ * Sets whether the tick marks are visible. Only used when the slider is in discrete mode.
+ *
+ * @param tickVisible The visibility of tick marks.
+ */
+ public void setTickVisible(boolean tickVisible) {
+ if (tickVisible != mTickVisible) {
+ mTickVisible = tickVisible;
+ notifyChanged();
+ }
+ }
+
+ /**
* Gets whether the current {@link Slider} value is displayed to the user.
*
* @return Whether the current {@link Slider} value is displayed to the user
@@ -519,7 +550,16 @@ public class SliderPreference extends Preference {
if (sliderValue != mSliderValue) {
if (callChangeListener(sliderValue)) {
setValueInternal(sliderValue, false);
- // TODO: mHapticFeedbackMode
+ switch (mHapticFeedbackMode) {
+ case HAPTIC_FEEDBACK_MODE_ON_TICKS:
+ slider.performHapticFeedback(CLOCK_TICK);
+ break;
+ case HAPTIC_FEEDBACK_MODE_ON_ENDS:
+ if (mSliderValue == mMax || mSliderValue == mMin) {
+ slider.performHapticFeedback(CLOCK_TICK);
+ }
+ break;
+ }
} else {
slider.setValue(mSliderValue);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03cb1ffbdef1..1297aa3ff7d5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1510,6 +1510,9 @@
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
+ <!-- Warning message when the bluetooth key is missing. [CHAR_LIMIT=NONE] -->
+ <string name="bluetooth_key_missing_subtext">Can’t connect</string>
+
<!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] -->
<string name="media_transfer_wired_device_name">Wired audio device</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt
index a64e8cc07b15..348941335311 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("ktlint:standard:filename") // remove once we have more bindings
package com.android.settingslib
@@ -29,7 +28,8 @@ interface PrimarySwitchPreferenceBinding : PreferenceBinding {
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
super.bind(preference, metadata)
- (preference as PrimarySwitchPreference).apply {
+ // Could bind on PreferenceScreen
+ (preference as? PrimarySwitchPreference)?.apply {
isChecked = preferenceDataStore!!.getBoolean(key, false)
isSwitchEnabled = isEnabled
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 9d979019be58..bf6006b1eddc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -151,6 +151,18 @@ public class RestrictedPreferenceHelper {
UserHandle.myUserId());
}
+ /**
+ * Configures the user restriction that this preference will track. This is equivalent to
+ * specifying {@link R.styleable#RestrictedPreference_userRestriction} in XML and allows
+ * configuring user restriction at runtime.
+ */
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mAttrUserRestriction = userRestriction == null ||
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction,
+ UserHandle.myUserId()) ? null : userRestriction;
+ setDisabledByAdmin(checkRestrictionEnforced());
+ }
+
public void useAdminDisabledSummary(boolean useSummary) {
mDisabledSummary = useSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 3646842d36ef..011b2fc15807 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1495,6 +1495,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
int leftBattery = -1;
int rightBattery = -1;
+ Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mDevice);
+ if (keyMissingCount != null && keyMissingCount > 0) {
+ return mContext.getString(R.string.bluetooth_key_missing_subtext);
+ }
+
if (isProfileConnectedFail() && isConnected()) {
return mContext.getString(R.string.profile_connect_timeout_subtext);
}
@@ -1863,10 +1868,31 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
+ " mIsLeAudioProfileConnectedFail=" + mIsLeAudioProfileConnectedFail
+ " mIsHeadsetProfileConnectedFail=" + mIsHeadsetProfileConnectedFail
+ " isConnectedSapDevice()=" + isConnectedSapDevice());
-
- return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail
- || (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail)
- || mIsLeAudioProfileConnectedFail;
+ if (mIsA2dpProfileConnectedFail) {
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null && a2dpProfile.isEnabled(mDevice)) {
+ return true;
+ }
+ }
+ if (mIsHearingAidProfileConnectedFail) {
+ HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+ if (hearingAidProfile != null && hearingAidProfile.isEnabled(mDevice)) {
+ return true;
+ }
+ }
+ if (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail) {
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if (headsetProfile != null && headsetProfile.isEnabled(mDevice)) {
+ return true;
+ }
+ }
+ if (mIsLeAudioProfileConnectedFail) {
+ LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
+ if (leAudioProfile != null && leAudioProfile.isEnabled(mDevice)) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 08f7806207db..01d8694256f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -89,11 +89,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
"com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE";
public static final String ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED =
"com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_DEVICE_CONNECTED";
+ public static final String ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED =
+ "com.android.settings.action.BLUETOOTH_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED";
public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE";
public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE";
public static final String EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE = "BT_DEVICE_TO_AUTO_ADD_SOURCE";
public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
public static final String EXTRA_PAIR_AND_JOIN_SHARING = "PAIR_AND_JOIN_SHARING";
+ public static final String EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA = "RECEIVE_DATA";
public static final String BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID =
"bluetooth_le_broadcast_primary_device_group_id";
public static final int BROADCAST_STATE_UNKNOWN = 0;
@@ -721,7 +724,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
Log.d(TAG, "The BluetoothLeBroadcast is null");
return null;
}
- if (mBluetoothLeBroadcastMetadata == null) {
+ if (mBluetoothLeBroadcastMetadata == null
+ // mBroadcastId is updated when onBroadcastStarted, which is always before
+ // onBroadcastMetadataChanged, so mBroadcastId is always the latest broadcast info
+ || mBluetoothLeBroadcastMetadata.getBroadcastId() != mBroadcastId) {
final List<BluetoothLeBroadcastMetadata> metadataList =
mServiceBroadcast.getAllBroadcastMetadata();
mBluetoothLeBroadcastMetadata =
@@ -729,6 +735,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
.filter(i -> i.getBroadcastId() == mBroadcastId)
.findFirst()
.orElse(null);
+ Log.d(TAG, "getLatestBluetoothLeBroadcastMetadata for broadcast id " + mBroadcastId);
}
return mBluetoothLeBroadcastMetadata;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
new file mode 100644
index 000000000000..a284d2010195
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothDevice
+import android.os.Parcel
+import android.os.Parcelable
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED
+
+/**
+ * Data class representing information received in a private broadcast.
+ * This class encapsulates details about the sink device, source ID, broadcast ID, and the
+ * broadcast source state.
+ *
+ * @param sink The [BluetoothDevice] acting as the sink.
+ * @param sourceId The ID of the audio source.
+ * @param broadcastId The ID of the broadcast source.
+ * @param programInfo The program info string of the broadcast source.
+ * @param state The current state of the broadcast source.
+ */
+data class PrivateBroadcastReceiveData(
+ val sink: BluetoothDevice?,
+ val sourceId: Int = -1,
+ val broadcastId: Int = -1,
+ val programInfo: String = "",
+ val state: LocalBluetoothLeBroadcastSourceState?,
+) : Parcelable {
+
+ override fun describeContents(): Int = 0
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeParcelable(sink, flags)
+ parcel.writeInt(sourceId)
+ parcel.writeInt(broadcastId)
+ parcel.writeString(programInfo)
+ parcel.writeSerializable(state)
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: Parcelable.Creator<PrivateBroadcastReceiveData> =
+ object : Parcelable.Creator<PrivateBroadcastReceiveData> {
+ override fun createFromParcel(parcel: Parcel) =
+ parcel.run {
+ PrivateBroadcastReceiveData(
+ sink = readParcelable(
+ BluetoothDevice::class.java.classLoader,
+ BluetoothDevice::class.java
+ ),
+ sourceId = readInt(),
+ broadcastId = readInt(),
+ programInfo = readString() ?: "",
+ state = readSerializable(
+ LocalBluetoothLeBroadcastSourceState::class.java.classLoader,
+ LocalBluetoothLeBroadcastSourceState::class.java
+ )
+ )
+ }
+ override fun newArray(size: Int): Array<PrivateBroadcastReceiveData?> {
+ return arrayOfNulls(size)
+ }
+ }
+
+ fun PrivateBroadcastReceiveData.isValid(): Boolean {
+ return sink != null
+ && sourceId != -1
+ && broadcastId != -1
+ && (state == STREAMING
+ || state == PAUSED
+ || state == DECRYPTION_FAILED)
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 000000000000..78dba57028ba
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()
+
+ private val fakeSecureSettings = FakeSecureSettings()
+ private val executor: Executor = Executor { it.run() }
+ private val configPerDeviceStateRotationLockDefaults = arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ )
+
+ @Mock
+ private lateinit var mockContext: Context
+
+ @Mock
+ private lateinit var mockContentResolver: ContentResolver
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Mock
+ private lateinit var mockHandler: Handler
+
+ @Mock
+ private lateinit var mockDeviceStateManager: DeviceStateManager
+
+ @Mock
+ private lateinit var mockResources: Resources
+ private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+ @Before
+ fun setUp() {
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(configPerDeviceStateRotationLockDefaults)
+ whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+ val runnable = invocation.arguments[0] as Runnable
+ runnable.run()
+ null
+ }
+ whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+ .thenReturn(mockDeviceStateManager)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+ .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mockPosturesHelper,
+ )
+ }
+
+ @Test
+ fun registerListener_onSettingsChanged_listenerNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener).onSettingsChanged()
+ }
+
+ @Test
+ fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+ val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener1)
+ settingManager.registerListener(listener2)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener1).onSettingsChanged()
+ verify(listener2).onSettingsChanged()
+ }
+
+ @Test
+ fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+ settingManager.unregisterListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener, never()).onSettingsChanged()
+ }
+
+ @Test
+ fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_onForFolded_returnsOn() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+ persistSettings("invalid_format")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+ persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+ val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+ assertThat(isAutoRotateOff).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+ val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+ assertThat(settableDeviceStates)
+ .containsExactly(
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+ )
+ }
+
+ private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+ persistSettings("$devicePosture:$autoRotateSetting")
+ }
+
+ private fun persistSettings(value: String) {
+ fakeSecureSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+ )
+ }
+
+ private companion object {
+ const val DEVICE_STATE_FOLDED = 0
+ const val DEVICE_STATE_HALF_FOLDED = 1
+ const val DEVICE_STATE_UNFOLDED = 2
+ const val DEVICE_STATE_REAR_DISPLAY = 3
+ const val DEVICE_STATE_INVALID = 4
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5ff83a..baebaf7dfef0 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
import com.google.common.truth.Expect;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 08484bceb1fb..8d9982fe7210 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -2,9 +2,9 @@ package com.android.settingslib.graph;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyFloat;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
index 9ab17c49bbec..b849919cb389 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
@@ -18,10 +18,10 @@ package com.android.settingslib.users;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index ab28d061419c..94bb61614411 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -16,11 +16,11 @@
package com.android.settingslib.users;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index 995314e44767..055487b79f78 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -22,7 +22,7 @@ import static com.android.settingslib.avatarpicker.AvatarPhotoController.REQUEST
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.never;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index e7533bc1044b..7f4d366b76d4 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -71,7 +71,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.Matchers;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -197,7 +197,7 @@ public class WifiTrackerTest {
.registerNetworkScoreCache(
anyInt(),
mScoreCacheCaptor.capture(),
- Matchers.anyInt());
+ ArgumentMatchers.anyInt());
// Capture requested keys and count down latch if present
doAnswer(
@@ -213,7 +213,7 @@ public class WifiTrackerTest {
}
return true;
}
- }).when(mockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
+ }).when(mockNetworkScoreManager).requestScores(ArgumentMatchers.<NetworkKey[]>any());
// We use a latch to detect callbacks as Tracker initialization state often invokes
// callbacks
@@ -464,9 +464,9 @@ public class WifiTrackerTest {
startTracking(tracker);
verify(mockNetworkScoreManager)
.registerNetworkScoreCache(
- Matchers.anyInt(),
+ ArgumentMatchers.anyInt(),
mScoreCacheCaptor.capture(),
- Matchers.anyInt());
+ ArgumentMatchers.anyInt());
WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index ed53d8d04988..f57ee0c0930e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -182,6 +182,8 @@ public class CachedBluetoothDeviceTest {
updateProfileStatus(connectingProfile, BluetoothProfile.STATE_CONNECTING);
// Set connection policy
when(connectingProfile.getConnectionPolicy(mDevice)).thenReturn(connectionPolicy);
+ when(connectingProfile.isEnabled(mDevice))
+ .thenReturn(connectionPolicy > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
// Act & Assert:
// Get the expected connection summary.
@@ -191,6 +193,9 @@ public class CachedBluetoothDeviceTest {
@Test
public void onProfileStateChanged_testConnectingToDisconnected_policyAllowed_problem() {
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
String connectTimeoutString = mContext.getString(R.string.profile_connect_timeout_subtext);
testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
@@ -205,6 +210,9 @@ public class CachedBluetoothDeviceTest {
@Test
public void onProfileStateChanged_testConnectingToDisconnected_policyForbidden_noProblem() {
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null);
testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile,
@@ -217,6 +225,9 @@ public class CachedBluetoothDeviceTest {
@Test
public void onProfileStateChanged_testConnectingToDisconnected_policyUnknown_noProblem() {
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null);
testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile,
@@ -1830,6 +1841,10 @@ public class CachedBluetoothDeviceTest {
@Test
public void getConnectionSummary_profileConnectedFail_showErrorMessage() {
final A2dpProfile profile = mock(A2dpProfile.class);
+ when(mProfileManager.getA2dpProfile()).thenReturn(profile);
+ when(profile.getConnectionPolicy(mDevice))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ when(profile.isEnabled(mDevice)).thenReturn(true);
mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
@@ -1842,6 +1857,10 @@ public class CachedBluetoothDeviceTest {
@Test
public void getTvConnectionSummary_profileConnectedFail_showErrorMessage() {
final A2dpProfile profile = mock(A2dpProfile.class);
+ when(mProfileManager.getA2dpProfile()).thenReturn(profile);
+ when(profile.getConnectionPolicy(mDevice))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ when(profile.isEnabled(mDevice)).thenReturn(true);
mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
new file mode 100644
index 000000000000..5fd67a16a305
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.os.Parcel
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
+import com.android.settingslib.bluetooth.PrivateBroadcastReceiveData.Companion.isValid
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class PrivateBroadcastReceiveDataTest {
+
+ @Test
+ fun parcelable() {
+ val original = PrivateBroadcastReceiveData(
+ sink = sink,
+ sourceId = 1,
+ broadcastId = 2,
+ programInfo = "Test Program",
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ )
+
+ val parcel = Parcel.obtain()
+ original.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ val recreated = PrivateBroadcastReceiveData.CREATOR.createFromParcel(parcel)
+
+ assertEquals(original, recreated)
+ }
+
+ @Test
+ fun isValid_validData() {
+ val data = PrivateBroadcastReceiveData(
+ sink = sink,
+ sourceId = 1,
+ broadcastId = 2,
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ )
+ assertTrue(data.isValid())
+ }
+
+ @Test
+ fun isValid_nullSink() {
+ val data = PrivateBroadcastReceiveData(
+ sink = null,
+ sourceId = 1,
+ broadcastId = 2,
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ )
+ assertFalse(data.isValid())
+ }
+
+ @Test
+ fun isValid_invalidSourceId() {
+ val data = PrivateBroadcastReceiveData(
+ sink = sink,
+ sourceId = -1,
+ broadcastId = 2,
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ )
+ assertFalse(data.isValid())
+ }
+
+ @Test
+ fun isValid_invalidBroadcastId() {
+ val data = PrivateBroadcastReceiveData(
+ sink = sink,
+ sourceId = 1,
+ broadcastId = -1,
+ state = LocalBluetoothLeBroadcastSourceState.STREAMING
+ )
+ assertFalse(data.isValid())
+ }
+
+ @Test
+ fun isValid_nullState() {
+ val data = PrivateBroadcastReceiveData(
+ sink = sink,
+ sourceId = 1,
+ broadcastId = 2,
+ state = null
+ )
+ assertFalse(data.isValid())
+ }
+
+ @Test
+ fun isValid_correctStates() {
+ assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.STREAMING).isValid())
+ assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.PAUSED).isValid())
+ assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED).isValid())
+ }
+
+ private companion object {
+ const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
+
+ val sink: BluetoothDevice =
+ BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(
+ TEST_DEVICE_ADDRESS,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM
+ )
+ }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index d929b0de391a..94aa955f0282 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -341,4 +341,7 @@
<!-- The default ringer mode. See `AudioManager` for list of valid values. -->
<integer name="def_ringer_mode">2</integer>
+
+ <!-- Caps minsum contrast from -1.0 (Material API) to 0.0 (Android Support)-->
+ <bool name="config_increaseMinContrast">true</bool>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fc61b1e875f3..d3291b4bac17 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -118,6 +118,8 @@ public class GlobalSettings {
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
Settings.Global.Wearable.AUTO_BEDTIME_MODE,
+ Settings.Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE,
+ Settings.Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE,
Settings.Global.FORCE_ENABLE_PSS_PROFILING,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index a2291123e192..829d4cb6c772 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -221,6 +221,8 @@ public class SecureSettings {
Settings.Secure.EMERGENCY_GESTURE_ENABLED,
Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED,
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED,
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED,
Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
@@ -298,5 +300,9 @@ public class SecureSettings {
Settings.Secure.DUAL_SHADE,
Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED,
Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED,
+ Settings.Secure.SPELL_CHECKER_ENABLED,
+ Settings.Secure.SELECTED_SPELL_CHECKER,
+ // SELECTED_SPELL_CHECKER_SUBTYPE needs to be restored after SELECTED_SPELL_CHECKER
+ Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index cf0447f9fb3a..98f5face5e96 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -124,7 +124,8 @@ public class SystemSettings {
Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
Settings.System.NOTIFICATION_COOLDOWN_ALL,
Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
- Settings.System.PREFERRED_REGION
+ Settings.System.PREFERRED_REGION,
+ Settings.System.CV_ENABLED
));
if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4c6a1ba7db0a..cd6521ff0dc5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -474,5 +474,7 @@ public class GlobalSettingsValidators {
String.valueOf(
Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
}));
+ VALIDATORS.put(Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index a4325344709a..d0f84627f8d8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -358,6 +358,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(
Secure.EMERGENCY_GESTURE_UI_LAST_STARTED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
@@ -470,5 +472,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SPELL_CHECKER_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER, NULLABLE_COMPONENT_NAME_VALIDATOR);
+ VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER_SUBTYPE, ANY_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4f649ed49be3..3a584401ed72 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -271,5 +271,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(System.CV_ENABLED,
+ new InclusiveIntegerRangeValidator(0, 1));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
index b0086c180cbd..78c87b389dfc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -1,2 +1,2 @@
-per-file WritableNamespacePrefixes.java = mpgroover@google.com,tedbauer@google.com
-per-file WritableNamespaces.java = mpgroover@google.com,tedbauer@google.com
+per-file WritableNamespacePrefixes.java = mpgroover@google.com
+per-file WritableNamespaces.java = mpgroover@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index fc402d45c3ec..37ada933259e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -37,10 +37,12 @@ import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.database.Cursor;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
@@ -941,6 +943,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
int restoredSettingsCount = 0;
+ boolean selectedSpellCheckerRestored = false;
for (String key : allowlist.mSettingsAllowlist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
@@ -1068,6 +1071,25 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
continue;
}
+ } else if (Settings.Secure.SELECTED_SPELL_CHECKER.equals(key)) {
+ ServiceInfo si = getServiceInfoOrNull(value);
+ if (si == null || si.applicationInfo == null) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+ + "as it is not installed");
+ continue;
+ } else if (!si.applicationInfo.isSystemApp()
+ && !si.applicationInfo.isUpdatedSystemApp()) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+ + "as it is not a system app");
+ continue;
+ }
+ selectedSpellCheckerRestored = true;
+ } else if (Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE.equals(key)) {
+ if (!selectedSpellCheckerRestored) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker_subtype "
+ + "as selected_spell_checker was not restored");
+ continue;
+ }
}
if (Settings.System.FONT_SCALE.equals(key)) {
@@ -1085,6 +1107,21 @@ public class SettingsBackupAgent extends BackupAgentHelper {
Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
}
+ if (Settings.Secure.CONTRAST_LEVEL.equals(key)) {
+ boolean increaseMinContrast = getBaseContext().getResources()
+ .getBoolean(R.bool.config_increaseMinContrast);
+
+ float valueFloat;
+ try {
+ valueFloat = Float.parseFloat(value);
+ } catch (NumberFormatException e) {
+ valueFloat = 0.0f;
+ }
+
+ float newValue = Math.max(valueFloat, increaseMinContrast ? 0.0f : -1.0f);
+ value = String.valueOf(newValue);
+ }
+
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
@@ -1868,6 +1905,18 @@ public class SettingsBackupAgent extends BackupAgentHelper {
return result;
}
+ @Nullable
+ private ServiceInfo getServiceInfoOrNull(@Nullable String flattenedServiceName) {
+ if (flattenedServiceName == null) return null;
+ ComponentName componentName = ComponentName.unflattenFromString(flattenedServiceName);
+ if (componentName == null) return null;
+ try {
+ return getBaseContext().getPackageManager().getServiceInfo(componentName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
/**
* Store the allowlist of settings to be backed up and validators for them.
*/
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 57facdaa388c..584b21adbe77 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2111,6 +2111,12 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
SecureSettingsProto.ADAPTIVE_CONNECTIVITY_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED,
+ SecureSettingsProto.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED,
+ SecureSettingsProto.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED);
final long controlsToken = p.start(SecureSettingsProto.CONTROLS);
dumpSetting(s, p,
@@ -3151,6 +3157,12 @@ class SettingsProtoDumpUtil {
SystemSettingsProto.Volume.MASTER_BALANCE);
p.end(volumeToken);
+ final long systemDisplayToken = p.start(SystemSettingsProto.DISPLAY);
+ dumpSetting(s, p,
+ Settings.System.CV_ENABLED,
+ SystemSettingsProto.Display.CV_ENABLED);
+ p.end(systemDisplayToken);
+
dumpSetting(s, p,
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 70c042cb8eba..3148f22ab511 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -749,15 +749,12 @@ public class SettingsBackupTest {
Settings.Secure.SECURE_FRP_MODE,
Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
- Settings.Secure.SELECTED_SPELL_CHECKER, // Intentionally removed in Q
- Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, // Intentionally removed in Q
Settings.Secure.SETTINGS_CLASSNAME,
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
Settings.Secure.SLEEP_TIMEOUT,
Settings.Secure.SMS_DEFAULT_APPLICATION,
- Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q
Settings.Secure.TRUST_AGENTS_INITIALIZED,
Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 48c360b635ea..bc727d33ad4a 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -16,15 +16,15 @@
package com.android.providers.settings;
-import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
-import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
@@ -35,12 +35,10 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
-import android.annotation.Nullable;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -69,13 +67,11 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.window.flags.Flags;
-import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -331,6 +327,47 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
}
@Test
+ public void testOnRestore_minContrastLevelIsRestoredToZero() {
+ mAgentUnderTest = new TestFriendlySettingsBackupAgent() {
+ @Override
+ protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
+ return new HashSet<>();
+ }
+ };
+ mAgentUnderTest.attach(mContext);
+
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ String contrastLevelValue = "-1.0";
+ Map<String, String> settingsToRestore = Map.of(Settings.Secure.CONTRAST_LEVEL,
+ contrastLevelValue);
+
+ byte[] backupData = generateBackupData(settingsToRestore);
+ mAgentUnderTest.restoreSettings(
+ backupData,
+ /* pos */ 0,
+ backupData.length,
+ Settings.Secure.CONTENT_URI,
+ null,
+ null,
+ null,
+ /* blockedSettingsArrayId */ 0,
+ Collections.emptySet(),
+ Collections.emptySet(),
+ KEY_SECURE);
+
+ // Check that the contrast level has been restored.
+ assertTrue(settingsHelper.mWrittenValues.containsKey(Settings.Secure.CONTRAST_LEVEL));
+
+ String restoredContrastLevel = settingsHelper.mWrittenValues.get(
+ Settings.Secure.CONTRAST_LEVEL);
+
+ float restoredFloat = Float.parseFloat(restoredContrastLevel);
+ assertEquals(0.0f, restoredFloat, 0.001f);
+ }
+
+ @Test
@DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
public void onCreate_metricsFlagIsDisabled_areAgentMetricsEnabledIsFalse() {
mAgentUnderTest.onCreate();
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index fb0678fedb56..5bba99f84d43 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -255,11 +255,11 @@ public class BugreportProgressService extends Service {
/** Always keep remote bugreport files created in the last day. */
private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
- /** Minimum delay for sending last update notification */
- private static final int DELAY_NOTIFICATION_MS = 250;
-
private final Object mLock = new Object();
+/** Minimum delay between percentage points before sending an update notification */
+ private static final int MIN_NOTIFICATION_GAP = 10;
+
/** Managed bugreport info (keyed by id) */
@GuardedBy("mLock")
private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
@@ -1460,17 +1460,6 @@ public class BugreportProgressService extends Service {
* Sends a notification indicating the bugreport has finished so use can share it.
*/
private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
-
- final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
- if (lastUpdate < DELAY_NOTIFICATION_MS) {
- Log.d(TAG, "Delaying final notification for "
- + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
- mMainThreadHandler.postDelayed(() -> {
- sendBugreportNotification(info, takingScreenshot);
- }, DELAY_NOTIFICATION_MS - lastUpdate);
- return;
- }
-
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(info);
@@ -1523,7 +1512,7 @@ public class BugreportProgressService extends Service {
builder.setSubText(info.getName());
}
- Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ Log.d(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
NotificationManager.from(mContext).notify(info.id, builder.build());
}
@@ -2753,6 +2742,11 @@ public class BugreportProgressService extends Service {
if (progress > CAPPED_PROGRESS) {
progress = CAPPED_PROGRESS;
}
+
+ if ((progress - info.lastProgress.intValue()) < MIN_NOTIFICATION_GAP) {
+ return;
+ }
+
if (DEBUG) {
if (progress != info.progress.intValue()) {
Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
index 433eca23080e..8031b103d72c 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
@@ -20,7 +20,7 @@ import static com.android.shell.BugreportProgressService.findSendToAccount;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.accounts.Account;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5b48566d92f9..129949fd38b2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -534,6 +534,7 @@ android_library {
"androidx.compose.animation_animation-graphics",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"kairos",
+ "displaylib",
"aconfig_settings_flags_lib",
],
libs: [
@@ -728,6 +729,7 @@ android_library {
"Traceur-res",
"aconfig_settings_flags_lib",
"kairos",
+ "displaylib",
],
}
@@ -770,6 +772,7 @@ android_library {
"androidx.compose.runtime_runtime",
"kairos",
"kosmos",
+ "displaylib",
"testables",
"androidx.test.rules",
"platform-compat-test-rules",
diff --git a/packages/SystemUI/TEST_OWNERS b/packages/SystemUI/TEST_OWNERS
index eadc86e386cb..21faf036c8f6 100644
--- a/packages/SystemUI/TEST_OWNERS
+++ b/packages/SystemUI/TEST_OWNERS
@@ -3,3 +3,6 @@
# for restructuring and test maintenance only
saff@google.com
+
+# Work around per-file set noparent includes
+per-file *=saff@google.com
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 71726199aeb6..1543dbe7bb29 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -46,7 +46,7 @@ import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.os.PowerManager;
import android.os.UserManager;
-import android.platform.uiautomator_helpers.WaitUtils;
+import android.platform.uiautomatorhelpers.WaitUtils;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 3cb30258fcb1..c6bc1c70ad18 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -155,3 +155,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "hearing_devices_input_routing_ui_improvement"
+ namespace: "accessibility"
+ description: "UI improvement for hearing device input routing feature"
+ bug: "397314200"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ab18612355f0..4693377654f8 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -213,18 +213,6 @@ flag {
}
flag {
- name: "notification_undo_guts_on_config_changed"
- namespace: "systemui"
- description: "Fixes a bug where a theme or font change while notification guts were open"
- " (e.g. the snooze options or notification info) would show an empty notification by"
- " closing the guts and undoing changes."
- bug: "379267630"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 694fc8eb6ad7..ca94482b9c5a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -261,7 +262,8 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
SurfaceControl.Transaction startTransaction
) {
checkArgument(isOpeningMode(launcherChange.getMode()));
- if (!isClosingType(info.getType())) {
+ if (!isClosingType(info.getType())
+ && !ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue()) {
return;
}
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 4a39cff388a9..4f01d7fcdb51 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -82,6 +82,10 @@ class TypefaceVariantCacheImpl(var baseTypeface: Typeface, override val animatio
}
}
+interface TextAnimatorListener : TextInterpolatorListener {
+ fun onInvalidate() {}
+}
+
/**
* This class provides text animation between two styles.
*
@@ -110,13 +114,19 @@ class TypefaceVariantCacheImpl(var baseTypeface: Typeface, override val animatio
class TextAnimator(
layout: Layout,
private val typefaceCache: TypefaceVariantCache,
- private val invalidateCallback: () -> Unit = {},
+ private val listener: TextAnimatorListener? = null,
) {
- @VisibleForTesting var textInterpolator = TextInterpolator(layout, typefaceCache)
+ var textInterpolator = TextInterpolator(layout, typefaceCache, listener)
@VisibleForTesting var createAnimator: () -> ValueAnimator = { ValueAnimator.ofFloat(1f) }
var animator: ValueAnimator? = null
+ val progress: Float
+ get() = textInterpolator.progress
+
+ val linearProgress: Float
+ get() = textInterpolator.linearProgress
+
val fontVariationUtils = FontVariationUtils()
sealed class PositionedGlyph {
@@ -288,8 +298,9 @@ class TextAnimator(
animator = buildAnimator(animation).apply { start() }
} else {
textInterpolator.progress = 1f
+ textInterpolator.linearProgress = 1f
textInterpolator.rebase()
- invalidateCallback()
+ listener?.onInvalidate()
}
}
@@ -302,7 +313,7 @@ class TextAnimator(
addUpdateListener {
textInterpolator.progress = it.animatedValue as Float
textInterpolator.linearProgress = it.currentPlayTime / it.duration.toFloat()
- invalidateCallback()
+ listener?.onInvalidate()
}
addListener(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 457f45388062..22c5258edb58 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -27,8 +27,19 @@ import android.util.MathUtils
import com.android.internal.graphics.ColorUtils
import java.lang.Math.max
+interface TextInterpolatorListener {
+ fun onPaintModified() {}
+
+ fun onRebased() {}
+}
+
/** Provide text style linear interpolation for plain text. */
-class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) {
+class TextInterpolator(
+ layout: Layout,
+ var typefaceCache: TypefaceVariantCache,
+ private val listener: TextInterpolatorListener? = null,
+) {
+
/**
* Returns base paint used for interpolation.
*
@@ -136,6 +147,7 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache)
*/
fun onTargetPaintModified() {
updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
+ listener?.onPaintModified()
}
/**
@@ -146,6 +158,7 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache)
*/
fun onBasePaintModified() {
updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
+ listener?.onPaintModified()
}
/**
@@ -204,6 +217,7 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache)
*/
fun rebase() {
if (progress == 0f) {
+ listener?.onRebased()
return
} else if (progress == 1f) {
basePaint.set(targetPaint)
@@ -233,6 +247,8 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache)
}
progress = 0f
+ linearProgress = 0f
+ listener?.onRebased()
}
/**
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index a352b1eebb81..82e5f5bb6dc8 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -21,7 +21,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -62,6 +61,7 @@ import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
@@ -82,6 +82,7 @@ import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.lifecycle.setViewTreeViewModelStoreOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
import com.android.systemui.animation.ComposableControllerFactory
@@ -291,7 +292,7 @@ fun Expandable(
.updateExpandableSize()
.then(minInteractiveSizeModifier)
.then(clickModifier(controller, onClick, interactionSource))
- .background(color, shape)
+ .animatedBackground(color, shape = shape)
.border(controller)
.onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
) {
@@ -307,19 +308,27 @@ private fun WrappedContent(
contentColor: Color,
content: @Composable (Expandable) -> Unit,
) {
- CompositionLocalProvider(LocalContentColor provides contentColor) {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
- // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
- // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
- // Expandable smaller than 40dp because if the expandable is not clickable directly, then
- // something in its content should be (and with a size >= 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
- content(expandable)
+ val minSizeContent =
+ @Composable {
+ // We make sure that the content itself (wrapped by the background) is at least 40.dp,
+ // which is the same as the M3 buttons. This applies even if onClick is null, to make it
+ // easier to write expandables that are sometimes clickable and sometimes not. There
+ // shouldn't be any Expandable smaller than 40dp because if the expandable is not
+ // clickable directly, then something in its content should be (and with a size >=
+ // 40dp).
+ val minSize = 40.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
}
+
+ if (contentColor.isSpecified) {
+ CompositionLocalProvider(LocalContentColor provides contentColor, content = minSizeContent)
+ } else {
+ minSizeContent()
}
}
@@ -345,7 +354,7 @@ private fun Modifier.expandable(
.thenIf(drawContent) {
Modifier.border(controller)
.then(clickModifier(controller, onClick, interactionSource))
- .background(controller.color, controller.shape)
+ .animatedBackground(controller.color, shape = controller.shape)
}
.onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
.drawWithContent {
@@ -422,7 +431,7 @@ private class DrawExpandableInOverlayNode(
// Background.
this@draw.drawBackground(
state,
- controller.color,
+ controller.color(),
controller.borderStroke,
size = Size(state.width.toFloat(), state.height.toFloat()),
)
@@ -469,7 +478,7 @@ private fun clickModifier(
/** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
@Composable
private fun AnimatedContentInOverlay(
- color: Color,
+ color: () -> Color,
sizeInOriginalLayout: Size,
overlay: ViewGroupOverlay,
controller: ExpandableControllerImpl,
@@ -523,7 +532,7 @@ private fun AnimatedContentInOverlay(
return@drawWithContent
}
- drawBackground(animatorState, color, controller.borderStroke)
+ drawBackground(animatorState, color(), controller.borderStroke)
drawContent()
},
// We center the content in the expanding container.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 72da175e26cf..d7d5a48e2b79 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -26,7 +26,6 @@ import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -80,6 +79,24 @@ fun rememberExpandableController(
borderStroke: BorderStroke? = null,
transitionControllerFactory: ComposableControllerFactory? = null,
): ExpandableController {
+ return rememberExpandableController(
+ color = { color },
+ shape = shape,
+ contentColor = contentColor,
+ borderStroke = borderStroke,
+ transitionControllerFactory = transitionControllerFactory,
+ )
+}
+
+/** Create an [ExpandableController] to control an [Expandable]. */
+@Composable
+fun rememberExpandableController(
+ color: () -> Color,
+ shape: Shape,
+ contentColor: Color = Color.Unspecified,
+ borderStroke: BorderStroke? = null,
+ transitionControllerFactory: ComposableControllerFactory? = null,
+): ExpandableController {
val composeViewRoot = LocalView.current
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
@@ -125,7 +142,7 @@ fun rememberExpandableController(
}
internal class ExpandableControllerImpl(
- internal val color: Color,
+ internal val color: () -> Color,
internal val contentColor: Color,
internal val shape: Shape,
internal val borderStroke: BorderStroke?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 959f28f2d99e..2ea9c487c27c 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -95,13 +95,31 @@ interface NestedDraggable {
* nested scrollable.
*
* This is called whenever a nested scrollable does not consume some scroll amount. If this
- * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
* consume all future events during preScroll until the nested scroll is finished.
*/
- fun shouldConsumeNestedScroll(sign: Float): Boolean
+ fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true
+
+ /**
+ * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
+ * be consumed by a nested scrollable.
+ *
+ * This is called before a nested scrollable is able to consume that scroll amount. If this
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
+ * consume all future scroll events during preScroll until the nested scroll is finished.
+ */
+ fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false
interface Controller {
/**
+ * Whether drags that were started from nested scrolls should be automatically
+ * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
+ * [onDrag].
+ */
+ val autoStopNestedDrags: Boolean
+ get() = false
+
+ /**
* Drag by [delta] pixels.
*
* @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
@@ -540,6 +558,14 @@ private class NestedDraggableNode(
}
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val sign = available.toFloat().sign
+ maybeCreateNewController(
+ sign = sign,
+ condition = {
+ source == NestedScrollSource.UserInput &&
+ draggable.shouldConsumeNestedPreScroll(sign)
+ },
+ )
val controller = nestedScrollController ?: return Offset.Zero
return scrollWithOverscroll(controller, available)
}
@@ -560,33 +586,46 @@ private class NestedDraggableNode(
}
val sign = offset.sign
+ maybeCreateNewController(
+ sign,
+ condition = { draggable.shouldConsumeNestedPostScroll(sign) },
+ )
+ val controller = nestedScrollController ?: return Offset.Zero
+ return scrollWithOverscroll(controller, available)
+ }
+
+ private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
if (
- nestedDragsEnabled &&
- nestedScrollController == null &&
- // TODO(b/388231324): Remove this.
- !lastEventWasScrollWheel &&
- draggable.shouldConsumeNestedScroll(sign) &&
- lastFirstDown != null
+ !nestedDragsEnabled ||
+ nestedScrollController != null ||
+ lastEventWasScrollWheel ||
+ lastFirstDown == null ||
+ !condition()
) {
- val startedPosition = checkNotNull(lastFirstDown)
-
- // TODO(b/382665591): Ensure that there is at least one pointer down.
- val pointersDownCount = pointersDown.size.coerceAtLeast(1)
- val pointerType = pointersDown.entries.firstOrNull()?.value
- nestedScrollController =
- NestedScrollController(
- overscrollEffect,
- draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
- )
+ return
}
- val controller = nestedScrollController ?: return Offset.Zero
- return scrollWithOverscroll(controller, available)
+ // TODO(b/382665591): Ensure that there is at least one pointer down.
+ val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+ val pointerType = pointersDown.entries.firstOrNull()?.value
+ val startedPosition = checkNotNull(lastFirstDown)
+ nestedScrollController =
+ NestedScrollController(
+ overscrollEffect,
+ draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
+ )
}
private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
- return scrollWithOverscroll(offset) {
- controller.controller.onDrag(it.toFloat()).toOffset()
+ return scrollWithOverscroll(offset) { delta ->
+ val available = delta.toFloat()
+ val consumed = controller.controller.onDrag(available)
+ if (controller.controller.autoStopNestedDrags && consumed != available) {
+ controller.ensureOnDragStoppedIsCalled()
+ this.nestedScrollController = null
+ }
+
+ consumed.toOffset()
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
new file mode 100644
index 000000000000..5b1f0a7c6eb6
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated.
+ *
+ * @param color color to paint background with
+ * @param alpha alpha of the background
+ * @param shape desired shape of the background
+ */
+fun Modifier.animatedBackground(
+ color: () -> Color,
+ alpha: () -> Float = DefaultAlpha,
+ shape: Shape = RectangleShape,
+) =
+ this.then(
+ BackgroundElement(
+ color = color,
+ alpha = alpha,
+ shape = shape,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "background"
+ value = color
+ properties["color"] = color
+ properties["alpha"] = alpha
+ properties["shape"] = shape
+ },
+ )
+ )
+
+private val DefaultAlpha = { 1f }
+
+private class BackgroundElement(
+ private val color: () -> Color,
+ private val alpha: () -> Float,
+ private val shape: Shape,
+ private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<BackgroundNode>() {
+ override fun create(): BackgroundNode {
+ return BackgroundNode(color, alpha, shape)
+ }
+
+ override fun update(node: BackgroundNode) {
+ node.color = color
+ node.alpha = alpha
+ node.shape = shape
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ inspectorInfo()
+ }
+
+ override fun hashCode(): Int {
+ var result = color.hashCode()
+ result = 31 * result + alpha.hashCode()
+ result = 31 * result + shape.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val otherModifier = other as? BackgroundElement ?: return false
+ return color == otherModifier.color &&
+ alpha == otherModifier.alpha &&
+ shape == otherModifier.shape
+ }
+}
+
+private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) :
+ DrawModifierNode, Modifier.Node(), ObserverModifierNode {
+
+ // Naively cache outline calculation if input parameters are the same, we manually observe
+ // reads inside shape#createOutline separately
+ private var lastSize: Size = Size.Unspecified
+ private var lastLayoutDirection: LayoutDirection? = null
+ private var lastOutline: Outline? = null
+ private var lastShape: Shape? = null
+ private var tmpOutline: Outline? = null
+
+ override fun ContentDrawScope.draw() {
+ if (shape === RectangleShape) {
+ // shortcut to avoid Outline calculation and allocation
+ drawRect()
+ } else {
+ drawOutline()
+ }
+ drawContent()
+ }
+
+ override fun onObservedReadsChanged() {
+ // Reset cached properties
+ lastSize = Size.Unspecified
+ lastLayoutDirection = null
+ lastOutline = null
+ lastShape = null
+ // Invalidate draw so we build the cache again - this is needed because observeReads within
+ // the draw scope obscures the state reads from the draw scope's observer
+ invalidateDraw()
+ }
+
+ private fun ContentDrawScope.drawRect() {
+ drawRect(color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.drawOutline() {
+ val outline = getOutline()
+ drawOutline(outline, color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.getOutline(): Outline {
+ val outline: Outline?
+ if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
+ outline = lastOutline!!
+ } else {
+ // Manually observe reads so we can directly invalidate the outline when it changes
+ // Use tmpOutline to avoid creating an object reference to local var outline
+ observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+ outline = tmpOutline
+ tmpOutline = null
+ }
+ lastOutline = outline
+ lastSize = size
+ lastLayoutDirection = layoutDirection
+ lastShape = shape
+ return outline!!
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
deleted file mode 100644
index 794b7a4a3d30..000000000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compose.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.LayoutDirection
-
-/**
- * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
- *
- * @param color color to paint background with
- * @param alpha alpha of the background
- * @param shape desired shape of the background
- */
-fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
- this.then(
- FadingBackground(
- brush = SolidColor(color),
- alpha = alpha,
- shape = shape,
- inspectorInfo =
- debugInspectorInfo {
- name = "background"
- value = color
- properties["color"] = color
- properties["alpha"] = alpha
- properties["shape"] = shape
- },
- )
- )
-
-private class FadingBackground
-constructor(
- private val brush: Brush,
- private val shape: Shape,
- private val alpha: () -> Float,
- inspectorInfo: InspectorInfo.() -> Unit,
-) : DrawModifier, InspectorValueInfo(inspectorInfo) {
- // naive cache outline calculation if size is the same
- private var lastSize: Size? = null
- private var lastLayoutDirection: LayoutDirection? = null
- private var lastOutline: Outline? = null
-
- override fun ContentDrawScope.draw() {
- if (shape === RectangleShape) {
- // shortcut to avoid Outline calculation and allocation
- drawRect()
- } else {
- drawOutline()
- }
- drawContent()
- }
-
- private fun ContentDrawScope.drawRect() {
- drawRect(brush, alpha = alpha())
- }
-
- private fun ContentDrawScope.drawOutline() {
- val outline =
- if (size == lastSize && layoutDirection == lastLayoutDirection) {
- lastOutline!!
- } else {
- shape.createOutline(size, layoutDirection, this)
- }
- drawOutline(outline, brush = brush, alpha = alpha())
- lastOutline = outline
- lastSize = size
- lastLayoutDirection = layoutDirection
- }
-
- override fun hashCode(): Int {
- var result = brush.hashCode()
- result = 31 * result + alpha.hashCode()
- result = 31 * result + shape.hashCode()
- return result
- }
-
- override fun equals(other: Any?): Boolean {
- val otherModifier = other as? FadingBackground ?: return false
- return brush == otherModifier.brush &&
- alpha == otherModifier.alpha &&
- shape == otherModifier.shape
- }
-
- override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
-}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index d8e46ad34c37..b247993de4e4 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -971,6 +971,68 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
}
+ @Test
+ fun consumeNestedPreScroll() {
+ var consumeNestedPreScroll by mutableStateOf(false)
+ val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ // Always consume everything so that the only way to start the drag is to
+ // intercept preScroll events.
+ .scrollable(rememberScrollableState { it }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ consumeNestedPreScroll = true
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ }
+
+ @Test
+ fun autoStopNestedDrags() {
+ var consumeScrolls by mutableStateOf(true)
+ val draggable =
+ TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .scrollable(rememberScrollableState { 0f }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy(50f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ consumeScrolls = false
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
@@ -996,7 +1058,9 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
{ velocity, _ ->
velocity
},
- private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+ private val autoStopNestedDrags: Boolean = false,
) : NestedDraggable {
var shouldStartDrag = true
var onDragStartedCalled = false
@@ -1026,6 +1090,8 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
onDragStarted.invoke(position, sign)
return object : NestedDraggable.Controller {
+ override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+
override fun onDrag(delta: Float): Float {
onDragCalled = true
onDragDelta += delta
@@ -1042,8 +1108,12 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
}
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
- return shouldConsumeNestedScroll.invoke(sign)
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPostScroll.invoke(sign)
+ }
+
+ override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPreScroll.invoke(sign)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 216f0a74e1c7..7782705d4c61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.animation.scene.ContentScope
-import com.android.compose.modifiers.fadingBackground
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.theme.colorAttr
import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.animation.Expandable
@@ -172,8 +172,8 @@ fun FooterActions(
val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
val backgroundModifier =
remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) {
- Modifier.fadingBackground(
- backgroundColor,
+ Modifier.animatedBackground(
+ { backgroundColor },
backgroundAlphaValue,
RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7015f79e4a9f..0daef465bf1f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -101,7 +101,7 @@ fun SceneContainer(
rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
val hapticFeedback = LocalHapticFeedback.current
- val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion()
+ val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion(isFullWidthShade())
val sceneTransitions =
remember(hapticFeedback, shadeExpansionMotion) {
transitionsBuilder.build(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 9b45ef693ce6..2f5a0306c84f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -6,7 +6,7 @@ import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
import com.android.internal.jank.Cuj
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
@@ -50,7 +50,7 @@ import com.android.systemui.shade.ui.composable.Shade
*/
class SceneContainerTransitions : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions {
return transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
index eb5548d45247..4c9c23ad9f9f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
@@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.composable
import com.android.compose.animation.scene.SceneTransitions
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
/**
* Builder of the comprehensive definition of all transitions between scenes and overlays in the
@@ -29,7 +29,7 @@ interface SceneContainerTransitionsBuilder {
/** Build the [SceneContainer] transitions spec. */
fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions
}
@@ -42,7 +42,7 @@ class ConstantSceneContainerTransitionsBuilder(
private val transitions: SceneTransitions = transitions { /* No transitions */ }
) : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions = transitions
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 9b4b91eb23c1..85aad9b087f1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -20,7 +20,7 @@ import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.notifications.ui.composable.NotificationsShade
import com.android.systemui.scene.shared.model.Overlays
@@ -29,7 +29,7 @@ import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toNotificationsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 47dd85f17cee..8f0447d05036 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -20,14 +20,14 @@ import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.qs.ui.composable.QuickSettingsShade
import com.android.systemui.shade.ui.composable.OverlayShade
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toQuickSettingsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 3446081fb123..068218a0053a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -53,8 +53,8 @@ import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import com.android.systemui.res.R
import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
@@ -114,9 +114,9 @@ private fun ContentScope.Panel(
modifier =
modifier
.disableSwipesWhenScrolling()
- .edgeContainerExpansionBackground(
- OverlayShade.Colors.PanelBackground,
- rememberShadeExpansionMotion(),
+ .verticalExpandContainerBackground(
+ backgroundColor = OverlayShade.Colors.PanelBackground,
+ spec = rememberShadeExpansionMotion(isFullWidthShade()),
)
) {
Column {
@@ -202,8 +202,10 @@ object OverlayShade {
}
@Composable
- fun rememberShadeExpansionMotion(): EdgeContainerExpansionSpec {
+ fun rememberShadeExpansionMotion(isFullWidth: Boolean): VerticalExpandContainerSpec {
val radius = Dimensions.PanelCornerRadius
- return remember(radius) { EdgeContainerExpansionSpec(radius = radius) }
+ return remember(radius, isFullWidth) {
+ VerticalExpandContainerSpec(isFloating = !isFullWidth, radius = radius)
+ }
}
}
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 1360611ed814..024ca22069ae 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
@@ -68,7 +68,7 @@ internal class DraggableHandler(
return layoutImpl.swipeDetector.detectSwipe(change)
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
return this.enabled()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index 72f9bd5da742..734de34dcd2f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -29,7 +29,7 @@ import com.android.compose.animation.scene.transformation.CustomPropertyTransfor
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformationScope
import com.android.mechanics.MotionValue
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import kotlinx.coroutines.CoroutineScope
interface ContainerRevealHaptics {
@@ -53,7 +53,7 @@ interface ContainerRevealHaptics {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun TransitionBuilder.verticalContainerReveal(
container: ElementKey,
- motionSpec: EdgeContainerExpansionSpec,
+ motionSpec: VerticalExpandContainerSpec,
haptics: ContainerRevealHaptics,
) {
// Make the swipe distance be exactly the target height of the container.
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 2ab27af37700..d63450b27390 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -42,6 +42,7 @@ android_test {
"PlatformMotionTestingCompose",
"androidx.test.runner",
"androidx.test.ext.junit",
+ "platform-parametric-runner-lib",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 000000000000..5dbb01338090
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,624 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.6
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 8.4,
+ "y": 5.2
+ },
+ {
+ "x": 11.2,
+ "y": 5.2
+ },
+ {
+ "x": 13.6,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 16.8,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 286
+ },
+ {
+ "width": 150,
+ "height": 279.6
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 266.8
+ },
+ {
+ "width": 150,
+ "height": 260.4
+ },
+ {
+ "width": 150,
+ "height": 254
+ },
+ {
+ "width": 150,
+ "height": 247.6
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 228
+ },
+ {
+ "width": 150,
+ "height": 221.6
+ },
+ {
+ "width": 150,
+ "height": 215.2
+ },
+ {
+ "width": 150,
+ "height": 208.8
+ },
+ {
+ "width": 150,
+ "height": 202
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 170
+ },
+ {
+ "width": 150,
+ "height": 163.6
+ },
+ {
+ "width": 150,
+ "height": 157.2
+ },
+ {
+ "width": 150,
+ "height": 150.8
+ },
+ {
+ "width": 150,
+ "height": 144.4
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 124.8
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 99.2
+ },
+ {
+ "width": 150,
+ "height": 81.2
+ },
+ {
+ "width": 144,
+ "height": 62.8
+ },
+ {
+ "width": 138,
+ "height": 46.4
+ },
+ {
+ "width": 133.2,
+ "height": 32
+ },
+ {
+ "width": 129.6,
+ "height": 20.4
+ },
+ {
+ "width": 127.2,
+ "height": 12
+ },
+ {
+ "width": 125.2,
+ "height": 6.4
+ },
+ {
+ "width": 124,
+ "height": 2.8
+ },
+ {
+ "width": 123.2,
+ "height": 0.4
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99781144,
+ 0.87040234,
+ 0.6695792,
+ 0.48078007,
+ 0.33033127,
+ 0.22004372,
+ 0.1432175,
+ 0.09153092,
+ 0.057634592,
+ 0.035840213,
+ 0.022048414,
+ 0.013435662,
+ 0.008117795,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 000000000000..1543d186ea03
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,644 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.8
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 6.8,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 13.2,
+ "y": 5.2
+ },
+ {
+ "x": 15.2,
+ "y": 5.2
+ },
+ {
+ "x": 16.8,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 294
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 282.8
+ },
+ {
+ "width": 150,
+ "height": 278
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 268.4
+ },
+ {
+ "width": 150,
+ "height": 263.6
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 253.6
+ },
+ {
+ "width": 150,
+ "height": 248.8
+ },
+ {
+ "width": 150,
+ "height": 244
+ },
+ {
+ "width": 150,
+ "height": 239.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 224.8
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 214.8
+ },
+ {
+ "width": 150,
+ "height": 210
+ },
+ {
+ "width": 150,
+ "height": 205.2
+ },
+ {
+ "width": 150,
+ "height": 200.4
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 190.8
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 171.6
+ },
+ {
+ "width": 150,
+ "height": 166.8
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 147.2
+ },
+ {
+ "width": 150,
+ "height": 122
+ },
+ {
+ "width": 150,
+ "height": 95.2
+ },
+ {
+ "width": 146.8,
+ "height": 70.8
+ },
+ {
+ "width": 139.6,
+ "height": 50.8
+ },
+ {
+ "width": 134,
+ "height": 34
+ },
+ {
+ "width": 130,
+ "height": 19.2
+ },
+ {
+ "width": 127.2,
+ "height": 9.2
+ },
+ {
+ "width": 125.2,
+ "height": 2.8
+ },
+ {
+ "width": 123.6,
+ "height": 0
+ },
+ {
+ "width": 122.8,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99979615,
+ 0.8860379,
+ 0.6869267,
+ 0.4955439,
+ 0.34154767,
+ 0.22803628,
+ 0.14868057,
+ 0.09515619,
+ 0.059987247,
+ 0.037340224,
+ 0.02299112,
+ 0.01402092,
+ 0.008477271,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
index b6e423afc6c4..13f75d2adfb4 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
@@ -68,200 +68,200 @@
"type": "not_found"
},
{
- "x": 62.8,
- "y": 50
+ "x": 18,
+ "y": 5.2
},
{
- "x": 62.8,
- "y": 50
+ "x": 18,
+ "y": 5.2
},
{
- "x": 61.6,
- "y": 50
+ "x": 16.8,
+ "y": 5.2
},
{
- "x": 60.8,
- "y": 50
+ "x": 16,
+ "y": 5.2
},
{
- "x": 59.6,
- "y": 50
+ "x": 14.8,
+ "y": 5.2
},
{
- "x": 58.4,
- "y": 50
+ "x": 13.6,
+ "y": 5.2
},
{
- "x": 57.2,
- "y": 50
+ "x": 12.4,
+ "y": 5.2
},
{
- "x": 56,
- "y": 50
+ "x": 11.2,
+ "y": 5.2
},
{
- "x": 55.2,
- "y": 50
+ "x": 10.4,
+ "y": 5.2
},
{
- "x": 54,
- "y": 50
+ "x": 9.2,
+ "y": 5.2
},
{
- "x": 54,
- "y": 50
+ "x": 9.2,
+ "y": 5.2
},
{
- "x": 52.8,
- "y": 50
+ "x": 8,
+ "y": 5.2
},
{
- "x": 51.6,
- "y": 50
+ "x": 6.8,
+ "y": 5.2
},
{
- "x": 50.4,
- "y": 50
+ "x": 5.6,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
}
]
},
@@ -279,200 +279,200 @@
"type": "not_found"
},
{
- "width": 162.4,
+ "width": 124.4,
"height": 1.6
},
{
- "width": 162.4,
+ "width": 124.4,
"height": 1.6
},
{
- "width": 164.8,
+ "width": 126.8,
"height": 3.2
},
{
- "width": 166.8,
+ "width": 128.8,
"height": 4.8
},
{
- "width": 169.2,
+ "width": 131.2,
"height": 6.4
},
{
- "width": 171.6,
+ "width": 133.6,
"height": 8
},
{
- "width": 173.6,
+ "width": 135.6,
"height": 9.6
},
{
- "width": 176,
+ "width": 138,
"height": 11.2
},
{
- "width": 178,
+ "width": 140,
"height": 12.8
},
{
- "width": 180.4,
+ "width": 142.4,
"height": 14.4
},
{
- "width": 180.4,
+ "width": 142.4,
"height": 14.4
},
{
- "width": 182.8,
+ "width": 144.8,
"height": 16.4
},
{
- "width": 185.2,
+ "width": 147.2,
"height": 18
},
{
- "width": 187.2,
+ "width": 149.2,
"height": 19.6
},
{
- "width": 188,
+ "width": 150,
"height": 25.6
},
{
- "width": 188,
+ "width": 150,
"height": 36.4
},
{
- "width": 188,
+ "width": 150,
"height": 45.6
},
{
- "width": 188,
+ "width": 150,
"height": 59.2
},
{
- "width": 188,
+ "width": 150,
"height": 72.8
},
{
- "width": 188,
+ "width": 150,
"height": 79.6
},
{
- "width": 188,
+ "width": 150,
"height": 92.8
},
{
- "width": 188,
+ "width": 150,
"height": 104.4
},
{
- "width": 188,
+ "width": 150,
"height": 115.2
},
{
- "width": 188,
+ "width": 150,
"height": 125.2
},
{
- "width": 188,
+ "width": 150,
"height": 134.8
},
{
- "width": 188,
+ "width": 150,
"height": 143.2
},
{
- "width": 188,
+ "width": 150,
"height": 151.2
},
{
- "width": 188,
+ "width": 150,
"height": 158.8
},
{
- "width": 188,
+ "width": 150,
"height": 160
},
{
- "width": 188,
+ "width": 150,
"height": 167.2
},
{
- "width": 188,
+ "width": 150,
"height": 174.4
},
{
- "width": 188,
+ "width": 150,
"height": 180.8
},
{
- "width": 188,
+ "width": 150,
"height": 187.6
},
{
- "width": 188,
+ "width": 150,
"height": 188
},
{
- "width": 188,
- "height": 207.2
+ "width": 150,
+ "height": 200.4
},
{
- "width": 188,
- "height": 240
+ "width": 150,
+ "height": 218.4
},
{
- "width": 188,
- "height": 275.2
+ "width": 150,
+ "height": 236.8
},
{
- "width": 188,
- "height": 306.8
+ "width": 150,
+ "height": 253.2
},
{
- "width": 188,
- "height": 333.2
+ "width": 150,
+ "height": 266.8
},
{
- "width": 188,
- "height": 353.6
+ "width": 150,
+ "height": 277.2
},
{
- "width": 188,
- "height": 368.8
+ "width": 150,
+ "height": 284.8
},
{
- "width": 188,
- "height": 380
+ "width": 150,
+ "height": 290
},
{
- "width": 188,
- "height": 387.6
+ "width": 150,
+ "height": 294
},
{
- "width": 188,
- "height": 392.4
+ "width": 150,
+ "height": 296.4
},
{
- "width": 188,
- "height": 395.6
+ "width": 150,
+ "height": 298
},
{
- "width": 188,
- "height": 398
+ "width": 150,
+ "height": 298.8
},
{
- "width": 188,
- "height": 398.8
+ "width": 150,
+ "height": 299.6
},
{
- "width": 188,
- "height": 399.6
+ "width": 150,
+ "height": 299.6
},
{
- "width": 188,
- "height": 400
+ "width": 150,
+ "height": 300
}
]
},
@@ -494,12 +494,12 @@
0,
0,
0.0067873597,
- 0.0612576,
- 0.19080025,
+ 0.06125766,
+ 0.19080031,
0.39327443,
0.5711931,
- 0.70855826,
- 0.8074064,
+ 0.7085583,
+ 0.8074065,
0.8754226,
0.9207788,
0.95032376,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
new file mode 100644
index 000000000000..115483cf4013
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,434 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.4
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 6.4,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 13.6,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 17.2,
+ "y": 5.2
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 290
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 266
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 223.6
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 141.2
+ },
+ {
+ "width": 150,
+ "height": 104
+ },
+ {
+ "width": 147.6,
+ "height": 74
+ },
+ {
+ "width": 139.6,
+ "height": 50.8
+ },
+ {
+ "width": 133.6,
+ "height": 32
+ },
+ {
+ "width": 129.2,
+ "height": 15.6
+ },
+ {
+ "width": 126.4,
+ "height": 5.2
+ },
+ {
+ "width": 124.4,
+ "height": 0
+ },
+ {
+ "width": 123.2,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99479187,
+ 0.8575029,
+ 0.65572864,
+ 0.4691311,
+ 0.3215357,
+ 0.21380007,
+ 0.13896108,
+ 0.0887118,
+ 0.05580789,
+ 0.03467691,
+ 0.021318138,
+ 0.0129826665,
+ 0.007839739,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index 6dc5a0e79e81..f202fcd5f59c 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
@@ -64,104 +64,104 @@
"type": "not_found"
},
{
- "x": 62.4,
- "y": 50
+ "x": 17.6,
+ "y": 5.2
},
{
- "x": 61.2,
- "y": 50
+ "x": 16.4,
+ "y": 5.2
},
{
- "x": 59.2,
- "y": 50
+ "x": 14.4,
+ "y": 5.2
},
{
- "x": 57.2,
- "y": 50
+ "x": 12.4,
+ "y": 5.2
},
{
- "x": 54.8,
- "y": 50
+ "x": 10,
+ "y": 5.2
},
{
- "x": 52.4,
- "y": 50
+ "x": 7.6,
+ "y": 5.2
},
{
- "x": 52.4,
- "y": 50
+ "x": 7.6,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
}
]
},
@@ -194,104 +194,104 @@
"type": "not_found"
},
{
- "width": 163.2,
+ "width": 125.2,
"height": 2
},
{
- "width": 166,
+ "width": 128,
"height": 4.4
},
{
- "width": 170,
+ "width": 132,
"height": 6.8
},
{
- "width": 174,
+ "width": 136,
"height": 10
},
{
- "width": 178.4,
+ "width": 140.4,
"height": 13.2
},
{
- "width": 183.6,
+ "width": 145.6,
"height": 16.8
},
{
- "width": 183.6,
+ "width": 145.6,
"height": 16.8
},
{
- "width": 188,
- "height": 44.4
+ "width": 150,
+ "height": 36.8
},
{
- "width": 188,
- "height": 103.6
+ "width": 150,
+ "height": 81.2
},
{
- "width": 188,
- "height": 166
+ "width": 150,
+ "height": 126.8
},
{
- "width": 188,
- "height": 222.4
+ "width": 150,
+ "height": 168
},
{
- "width": 188,
- "height": 270
+ "width": 150,
+ "height": 202.8
},
{
- "width": 188,
- "height": 307.2
+ "width": 150,
+ "height": 230
},
{
- "width": 188,
- "height": 335.6
+ "width": 150,
+ "height": 250.8
},
{
- "width": 188,
- "height": 356.4
+ "width": 150,
+ "height": 266.4
},
{
- "width": 188,
- "height": 371.2
+ "width": 150,
+ "height": 277.6
},
{
- "width": 188,
- "height": 381.6
+ "width": 150,
+ "height": 285.2
},
{
- "width": 188,
- "height": 388.8
+ "width": 150,
+ "height": 290.4
},
{
- "width": 188,
- "height": 393.2
+ "width": 150,
+ "height": 294
},
{
- "width": 188,
- "height": 396
+ "width": 150,
+ "height": 296.4
},
{
- "width": 188,
- "height": 398
+ "width": 150,
+ "height": 298
},
{
- "width": 188,
- "height": 398.8
+ "width": 150,
+ "height": 298.8
},
{
- "width": 188,
- "height": 399.2
+ "width": 150,
+ "height": 299.2
},
{
- "width": 188,
- "height": 399.6
+ "width": 150,
+ "height": 299.6
},
{
- "width": 188,
- "height": 399.6
+ "width": 150,
+ "height": 299.6
}
]
},
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
index 1cd971aa2898..4c57bda8fd5a 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -89,268 +89,268 @@
"type": "not_found"
},
{
- "x": 62.8,
- "y": 50
+ "x": 18,
+ "y": 5.2
},
{
- "x": 62,
- "y": 50
+ "x": 17.2,
+ "y": 5.2
},
{
- "x": 61.2,
- "y": 50
+ "x": 16.4,
+ "y": 5.2
},
{
- "x": 60.4,
- "y": 50
+ "x": 15.6,
+ "y": 5.2
},
{
- "x": 59.6,
- "y": 50
+ "x": 14.8,
+ "y": 5.2
},
{
- "x": 58.8,
- "y": 50
+ "x": 14,
+ "y": 5.2
},
{
- "x": 58,
- "y": 50
+ "x": 13.2,
+ "y": 5.2
},
{
- "x": 57.2,
- "y": 50
+ "x": 12.4,
+ "y": 5.2
},
{
- "x": 56.4,
- "y": 50
+ "x": 11.6,
+ "y": 5.2
},
{
- "x": 55.6,
- "y": 50
+ "x": 10.8,
+ "y": 5.2
},
{
- "x": 55.2,
- "y": 50
+ "x": 10.4,
+ "y": 5.2
},
{
- "x": 54.4,
- "y": 50
+ "x": 9.6,
+ "y": 5.2
},
{
- "x": 53.6,
- "y": 50
+ "x": 8.8,
+ "y": 5.2
},
{
- "x": 53.2,
- "y": 50
+ "x": 8.4,
+ "y": 5.2
},
{
- "x": 52.8,
- "y": 50
+ "x": 8,
+ "y": 5.2
},
{
- "x": 52,
- "y": 50
+ "x": 7.2,
+ "y": 5.2
},
{
- "x": 51.6,
- "y": 50
+ "x": 6.8,
+ "y": 5.2
},
{
- "x": 51.2,
- "y": 50
+ "x": 6.4,
+ "y": 5.2
},
{
- "x": 50.8,
- "y": 50
+ "x": 6,
+ "y": 5.2
},
{
- "x": 50.4,
- "y": 50
+ "x": 5.6,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50.4,
- "y": 50
+ "x": 5.6,
+ "y": 5.2
},
{
- "x": 50.8,
- "y": 50
+ "x": 6,
+ "y": 5.2
},
{
- "x": 51.2,
- "y": 50
+ "x": 6.4,
+ "y": 5.2
},
{
- "x": 51.6,
- "y": 50
+ "x": 6.8,
+ "y": 5.2
},
{
- "x": 52,
- "y": 50
+ "x": 7.2,
+ "y": 5.2
},
{
- "x": 52.8,
- "y": 50
+ "x": 8,
+ "y": 5.2
},
{
- "x": 53.2,
- "y": 50
+ "x": 8.4,
+ "y": 5.2
},
{
- "x": 53.6,
- "y": 50
+ "x": 8.8,
+ "y": 5.2
},
{
- "x": 54.4,
- "y": 50
+ "x": 9.6,
+ "y": 5.2
},
{
- "x": 55.2,
- "y": 50
+ "x": 10.4,
+ "y": 5.2
},
{
- "x": 55.6,
- "y": 50
+ "x": 10.8,
+ "y": 5.2
},
{
- "x": 56.4,
- "y": 50
+ "x": 11.6,
+ "y": 5.2
},
{
- "x": 57.2,
- "y": 50
+ "x": 12.4,
+ "y": 5.2
},
{
- "x": 58,
- "y": 50
+ "x": 13.2,
+ "y": 5.2
},
{
- "x": 58.8,
- "y": 50
+ "x": 14,
+ "y": 5.2
},
{
- "x": 59.6,
- "y": 50
+ "x": 14.8,
+ "y": 5.2
},
{
- "x": 60.4,
- "y": 50
+ "x": 15.6,
+ "y": 5.2
},
{
- "x": 61.2,
- "y": 50
+ "x": 16.4,
+ "y": 5.2
},
{
- "x": 62,
- "y": 50
+ "x": 17.2,
+ "y": 5.2
},
{
- "x": 62.8,
- "y": 50
+ "x": 18,
+ "y": 5.2
},
{
- "x": 63.6,
- "y": 50
+ "x": 18.8,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
},
{
- "x": 64,
- "y": 50
+ "x": 19.2,
+ "y": 5.2
}
]
},
@@ -371,267 +371,267 @@
"type": "not_found"
},
{
- "width": 162.4,
+ "width": 124.4,
"height": 1.6
},
{
- "width": 164,
+ "width": 126,
"height": 2.8
},
{
- "width": 166,
+ "width": 128,
"height": 4
},
{
- "width": 167.6,
+ "width": 129.6,
"height": 5.2
},
{
- "width": 169.2,
+ "width": 131.2,
"height": 6.4
},
{
- "width": 170.8,
+ "width": 132.8,
"height": 7.6
},
{
- "width": 172.4,
+ "width": 134.4,
"height": 8.8
},
{
- "width": 174,
+ "width": 136,
"height": 10
},
{
- "width": 175.2,
+ "width": 137.2,
"height": 10.8
},
{
- "width": 176.8,
+ "width": 138.8,
"height": 12
},
{
- "width": 178,
+ "width": 140,
"height": 12.8
},
{
- "width": 179.2,
+ "width": 141.2,
"height": 13.6
},
{
- "width": 180.8,
+ "width": 142.8,
"height": 14.8
},
{
- "width": 182,
+ "width": 144,
"height": 15.6
},
{
- "width": 182.8,
+ "width": 144.8,
"height": 16.4
},
{
- "width": 184,
+ "width": 146,
"height": 17.2
},
{
- "width": 184.8,
+ "width": 146.8,
"height": 17.6
},
{
- "width": 186,
+ "width": 148,
"height": 18.4
},
{
- "width": 186.8,
+ "width": 148.8,
"height": 19.2
},
{
- "width": 187.6,
+ "width": 149.6,
"height": 19.6
},
{
- "width": 188,
+ "width": 150,
"height": 21.2
},
{
- "width": 188,
+ "width": 150,
"height": 24.8
},
{
- "width": 188,
+ "width": 150,
"height": 30
},
{
- "width": 188,
+ "width": 150,
"height": 38
},
{
- "width": 188,
+ "width": 150,
"height": 46
},
{
- "width": 188,
+ "width": 150,
"height": 54
},
{
- "width": 188,
+ "width": 150,
"height": 61.2
},
{
- "width": 188,
+ "width": 150,
"height": 66.8
},
{
- "width": 188,
+ "width": 150,
"height": 71.6
},
{
- "width": 188,
+ "width": 150,
"height": 75.6
},
{
- "width": 188,
+ "width": 150,
"height": 78
},
{
- "width": 188,
+ "width": 150,
"height": 79.6
},
{
- "width": 188,
+ "width": 150,
"height": 80.8
},
{
- "width": 188,
+ "width": 150,
"height": 80.8
},
{
- "width": 188,
+ "width": 150,
"height": 80.4
},
{
- "width": 188,
+ "width": 150,
"height": 79.6
},
{
- "width": 187.6,
+ "width": 149.6,
"height": 78
},
{
- "width": 186.8,
+ "width": 148.8,
"height": 76.4
},
{
- "width": 186,
+ "width": 148,
"height": 74
},
{
- "width": 184.8,
+ "width": 146.8,
"height": 71.6
},
{
- "width": 184,
+ "width": 146,
"height": 69.2
},
{
- "width": 182.8,
+ "width": 144.8,
"height": 66
},
{
- "width": 182,
+ "width": 144,
"height": 62.8
},
{
- "width": 180.8,
+ "width": 142.8,
"height": 59.2
},
{
- "width": 179.2,
+ "width": 141.2,
"height": 55.6
},
{
- "width": 178,
+ "width": 140,
"height": 52
},
{
- "width": 176.8,
+ "width": 138.8,
"height": 48
},
{
- "width": 175.2,
+ "width": 137.2,
"height": 44
},
{
- "width": 174,
+ "width": 136,
"height": 40
},
{
- "width": 172.4,
+ "width": 134.4,
"height": 37.6
},
{
- "width": 170.8,
+ "width": 132.8,
"height": 38
},
{
- "width": 169.2,
+ "width": 131.2,
"height": 30.4
},
{
- "width": 167.6,
+ "width": 129.6,
"height": 25.2
},
{
- "width": 166,
+ "width": 128,
"height": 20.4
},
{
- "width": 164,
+ "width": 126,
"height": 16
},
{
- "width": 162.4,
+ "width": 124.4,
"height": 12.4
},
{
- "width": 160.8,
+ "width": 122.8,
"height": 9.2
},
{
- "width": 160,
+ "width": 122,
"height": 6.8
},
{
- "width": 160,
+ "width": 122,
"height": 5.2
},
{
- "width": 160,
+ "width": 122,
"height": 3.6
},
{
- "width": 160,
+ "width": 122,
"height": 2.4
},
{
- "width": 160,
+ "width": 122,
"height": 1.6
},
{
- "width": 160,
+ "width": 122,
"height": 0.8
},
{
- "width": 160,
+ "width": 122,
"height": 0.4
},
{
- "width": 160,
+ "width": 122,
"height": 0.4
},
{
- "width": 160,
+ "width": 122,
"height": 0
}
]
@@ -658,10 +658,10 @@
0,
0.012518823,
0.0741024,
- 0.2254293,
+ 0.22542936,
0.42628878,
- 0.5976641,
- 0.7280312,
+ 0.5976642,
+ 0.7280313,
0.82100236,
0.8845844,
0.9267946,
@@ -706,17 +706,17 @@
1,
0.9944124,
0.9417388,
- 0.8184184,
- 0.6157812,
- 0.4361611,
- 0.2968906,
- 0.19641554,
- 0.12716137,
- 0.080921985,
- 0.050773025,
- 0.03147719,
- 0.019312752,
- 0.011740655,
+ 0.81841844,
+ 0.61578125,
+ 0.43616113,
+ 0.29689062,
+ 0.19641556,
+ 0.12716138,
+ 0.080922,
+ 0.050773032,
+ 0.031477194,
+ 0.019312754,
+ 0.011740657,
0
]
}
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 000000000000..26c80e331f81
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,314 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 7.2,
+ "y": 5.2
+ },
+ {
+ "x": 11.2,
+ "y": 5.2
+ },
+ {
+ "x": 14,
+ "y": 5.2
+ },
+ {
+ "x": 16,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 234.8
+ },
+ {
+ "width": 150,
+ "height": 185.2
+ },
+ {
+ "width": 150,
+ "height": 138.8
+ },
+ {
+ "width": 150,
+ "height": 100.4
+ },
+ {
+ "width": 146.4,
+ "height": 69.6
+ },
+ {
+ "width": 138.4,
+ "height": 46.8
+ },
+ {
+ "width": 132.4,
+ "height": 28
+ },
+ {
+ "width": 128.4,
+ "height": 13.2
+ },
+ {
+ "width": 125.6,
+ "height": 4
+ },
+ {
+ "width": 124,
+ "height": 0
+ },
+ {
+ "width": 122.8,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9762947,
+ 0.8118515,
+ 0.60931784,
+ 0.43090785,
+ 0.29299664,
+ 0.19368339,
+ 0.12531388,
+ 0.079705715,
+ 0.049988627,
+ 0.030979574,
+ 0.019001365,
+ 0.011548042,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 000000000000..7a02d369c7f2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,224 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 8,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 129.2,
+ "height": 5.2
+ },
+ {
+ "width": 144.4,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 62
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 166
+ },
+ {
+ "width": 150,
+ "height": 204
+ },
+ {
+ "width": 150,
+ "height": 233.2
+ },
+ {
+ "width": 150,
+ "height": 254.4
+ },
+ {
+ "width": 150,
+ "height": 270
+ },
+ {
+ "width": 150,
+ "height": 280.8
+ },
+ {
+ "width": 150,
+ "height": 288
+ },
+ {
+ "width": 150,
+ "height": 292.8
+ },
+ {
+ "width": 150,
+ "height": 296
+ },
+ {
+ "width": 150,
+ "height": 298
+ },
+ {
+ "width": 150,
+ "height": 298.8
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0951103,
+ 0.2911651,
+ 0.48551244,
+ 0.6439433,
+ 0.76157355,
+ 0.8441935,
+ 0.9001033,
+ 0.9369305,
+ 0.96069145,
+ 0.97577035,
+ 0.98520935,
+ 0.9910494,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 000000000000..f44d4cd7c14e
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,584 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.6
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 286
+ },
+ {
+ "width": 150,
+ "height": 279.6
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 266.8
+ },
+ {
+ "width": 150,
+ "height": 260.4
+ },
+ {
+ "width": 150,
+ "height": 254
+ },
+ {
+ "width": 150,
+ "height": 247.6
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 228
+ },
+ {
+ "width": 150,
+ "height": 221.6
+ },
+ {
+ "width": 150,
+ "height": 215.2
+ },
+ {
+ "width": 150,
+ "height": 208.8
+ },
+ {
+ "width": 150,
+ "height": 202
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 170
+ },
+ {
+ "width": 150,
+ "height": 163.6
+ },
+ {
+ "width": 150,
+ "height": 157.2
+ },
+ {
+ "width": 150,
+ "height": 150.8
+ },
+ {
+ "width": 150,
+ "height": 144.4
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 124.8
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 99.2
+ },
+ {
+ "width": 150,
+ "height": 84.4
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 58
+ },
+ {
+ "width": 150,
+ "height": 46.4
+ },
+ {
+ "width": 150,
+ "height": 36.4
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 15.6
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 8
+ },
+ {
+ "width": 150,
+ "height": 5.6
+ },
+ {
+ "width": 150,
+ "height": 3.6
+ },
+ {
+ "width": 150,
+ "height": 2.4
+ },
+ {
+ "width": 150,
+ "height": 1.2
+ },
+ {
+ "width": 150,
+ "height": 0.8
+ },
+ {
+ "width": 150,
+ "height": 0.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99781144,
+ 0.87040234,
+ 0.6695792,
+ 0.48078007,
+ 0.33033127,
+ 0.22004372,
+ 0.1432175,
+ 0.09153092,
+ 0.057634592,
+ 0.035840213,
+ 0.022048414,
+ 0.013435662,
+ 0.008117795,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 000000000000..9b68c71a7a34
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,624 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.8
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 294
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 282.8
+ },
+ {
+ "width": 150,
+ "height": 278
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 268.4
+ },
+ {
+ "width": 150,
+ "height": 263.6
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 253.6
+ },
+ {
+ "width": 150,
+ "height": 248.8
+ },
+ {
+ "width": 150,
+ "height": 244
+ },
+ {
+ "width": 150,
+ "height": 239.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 224.8
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 214.8
+ },
+ {
+ "width": 150,
+ "height": 210
+ },
+ {
+ "width": 150,
+ "height": 205.2
+ },
+ {
+ "width": 150,
+ "height": 200.4
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 190.8
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 171.6
+ },
+ {
+ "width": 150,
+ "height": 166.8
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 147.2
+ },
+ {
+ "width": 150,
+ "height": 122
+ },
+ {
+ "width": 150,
+ "height": 95.2
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 51.6
+ },
+ {
+ "width": 150,
+ "height": 36.8
+ },
+ {
+ "width": 150,
+ "height": 25.6
+ },
+ {
+ "width": 150,
+ "height": 17.2
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 4
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 0.8
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99979615,
+ 0.8860379,
+ 0.6869267,
+ 0.4955439,
+ 0.34154767,
+ 0.22803628,
+ 0.14868057,
+ 0.09515619,
+ 0.059987247,
+ 0.037340224,
+ 0.02299112,
+ 0.01402092,
+ 0.008477271,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 000000000000..1bf6a62d02af
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 3.2
+ },
+ {
+ "width": 150,
+ "height": 4.8
+ },
+ {
+ "width": 150,
+ "height": 6.4
+ },
+ {
+ "width": 150,
+ "height": 8
+ },
+ {
+ "width": 150,
+ "height": 9.6
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 12.8
+ },
+ {
+ "width": 150,
+ "height": 14.4
+ },
+ {
+ "width": 150,
+ "height": 14.4
+ },
+ {
+ "width": 150,
+ "height": 16.4
+ },
+ {
+ "width": 150,
+ "height": 18
+ },
+ {
+ "width": 150,
+ "height": 19.6
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 22.8
+ },
+ {
+ "width": 150,
+ "height": 24.4
+ },
+ {
+ "width": 150,
+ "height": 26
+ },
+ {
+ "width": 150,
+ "height": 27.6
+ },
+ {
+ "width": 150,
+ "height": 27.6
+ },
+ {
+ "width": 150,
+ "height": 29.2
+ },
+ {
+ "width": 150,
+ "height": 30.8
+ },
+ {
+ "width": 150,
+ "height": 32.4
+ },
+ {
+ "width": 150,
+ "height": 34
+ },
+ {
+ "width": 150,
+ "height": 40.4
+ },
+ {
+ "width": 150,
+ "height": 52.4
+ },
+ {
+ "width": 150,
+ "height": 64.8
+ },
+ {
+ "width": 150,
+ "height": 83.2
+ },
+ {
+ "width": 150,
+ "height": 96
+ },
+ {
+ "width": 150,
+ "height": 114.8
+ },
+ {
+ "width": 150,
+ "height": 132
+ },
+ {
+ "width": 150,
+ "height": 148
+ },
+ {
+ "width": 150,
+ "height": 162
+ },
+ {
+ "width": 150,
+ "height": 168.4
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 208
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 248.4
+ },
+ {
+ "width": 150,
+ "height": 263.2
+ },
+ {
+ "width": 150,
+ "height": 274.8
+ },
+ {
+ "width": 150,
+ "height": 283.2
+ },
+ {
+ "width": 150,
+ "height": 289.2
+ },
+ {
+ "width": 150,
+ "height": 293.6
+ },
+ {
+ "width": 150,
+ "height": 296
+ },
+ {
+ "width": 150,
+ "height": 298
+ },
+ {
+ "width": 150,
+ "height": 298.8
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 300
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.0067873597,
+ 0.06125766,
+ 0.19080031,
+ 0.39327443,
+ 0.5711931,
+ 0.7085583,
+ 0.8074065,
+ 0.8754226,
+ 0.9207788,
+ 0.95032376,
+ 0.9692185,
+ 0.98112255,
+ 0.9885286,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
new file mode 100644
index 000000000000..86805bd6ff29
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,424 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.4
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 290
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 266
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 223.6
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 141.2
+ },
+ {
+ "width": 150,
+ "height": 104
+ },
+ {
+ "width": 150,
+ "height": 72
+ },
+ {
+ "width": 150,
+ "height": 46
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 15.6
+ },
+ {
+ "width": 150,
+ "height": 7.2
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99479187,
+ 0.8575029,
+ 0.65572864,
+ 0.4691311,
+ 0.3215357,
+ 0.21380007,
+ 0.13896108,
+ 0.0887118,
+ 0.05580789,
+ 0.03467691,
+ 0.021318138,
+ 0.0129826665,
+ 0.007839739,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
new file mode 100644
index 000000000000..98519db9e848
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -0,0 +1,374 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 10
+ },
+ {
+ "width": 150,
+ "height": 13.2
+ },
+ {
+ "width": 150,
+ "height": 16.8
+ },
+ {
+ "width": 150,
+ "height": 16.8
+ },
+ {
+ "width": 150,
+ "height": 23.6
+ },
+ {
+ "width": 150,
+ "height": 32.8
+ },
+ {
+ "width": 150,
+ "height": 76.8
+ },
+ {
+ "width": 150,
+ "height": 123.6
+ },
+ {
+ "width": 150,
+ "height": 164.8
+ },
+ {
+ "width": 150,
+ "height": 198.4
+ },
+ {
+ "width": 150,
+ "height": 225.6
+ },
+ {
+ "width": 150,
+ "height": 246.4
+ },
+ {
+ "width": 150,
+ "height": 262
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 281.6
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 292
+ },
+ {
+ "width": 150,
+ "height": 294.8
+ },
+ {
+ "width": 150,
+ "height": 296.4
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 298.4
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.008216977,
+ 0.06259775,
+ 0.19032806,
+ 0.39281356,
+ 0.57081985,
+ 0.7082821,
+ 0.80721295,
+ 0.8752918,
+ 0.9206928,
+ 0.95026827,
+ 0.9691833,
+ 0.98110056,
+ 0.988515,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 000000000000..850cee9130d0
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,744 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976,
+ 992,
+ 1008,
+ 1024,
+ 1040,
+ 1056,
+ 1072,
+ 1088,
+ 1104,
+ 1120,
+ 1136
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 2.8
+ },
+ {
+ "width": 150,
+ "height": 4.8
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 8.4
+ },
+ {
+ "width": 150,
+ "height": 10.4
+ },
+ {
+ "width": 150,
+ "height": 12.4
+ },
+ {
+ "width": 150,
+ "height": 14
+ },
+ {
+ "width": 150,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 17.6
+ },
+ {
+ "width": 150,
+ "height": 19.2
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 22.4
+ },
+ {
+ "width": 150,
+ "height": 24
+ },
+ {
+ "width": 150,
+ "height": 25.6
+ },
+ {
+ "width": 150,
+ "height": 26.8
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 29.2
+ },
+ {
+ "width": 150,
+ "height": 30.4
+ },
+ {
+ "width": 150,
+ "height": 31.6
+ },
+ {
+ "width": 150,
+ "height": 32.4
+ },
+ {
+ "width": 150,
+ "height": 33.2
+ },
+ {
+ "width": 150,
+ "height": 34
+ },
+ {
+ "width": 150,
+ "height": 36.8
+ },
+ {
+ "width": 150,
+ "height": 42.4
+ },
+ {
+ "width": 150,
+ "height": 50.8
+ },
+ {
+ "width": 150,
+ "height": 64
+ },
+ {
+ "width": 150,
+ "height": 78
+ },
+ {
+ "width": 150,
+ "height": 91.2
+ },
+ {
+ "width": 150,
+ "height": 102.4
+ },
+ {
+ "width": 150,
+ "height": 112.4
+ },
+ {
+ "width": 150,
+ "height": 120
+ },
+ {
+ "width": 150,
+ "height": 126
+ },
+ {
+ "width": 150,
+ "height": 130
+ },
+ {
+ "width": 150,
+ "height": 132.8
+ },
+ {
+ "width": 150,
+ "height": 134
+ },
+ {
+ "width": 150,
+ "height": 134
+ },
+ {
+ "width": 150,
+ "height": 133.2
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 128.8
+ },
+ {
+ "width": 150,
+ "height": 125.2
+ },
+ {
+ "width": 150,
+ "height": 121.6
+ },
+ {
+ "width": 150,
+ "height": 117.6
+ },
+ {
+ "width": 150,
+ "height": 112.8
+ },
+ {
+ "width": 150,
+ "height": 108
+ },
+ {
+ "width": 150,
+ "height": 102.4
+ },
+ {
+ "width": 150,
+ "height": 96.4
+ },
+ {
+ "width": 150,
+ "height": 91.2
+ },
+ {
+ "width": 150,
+ "height": 88
+ },
+ {
+ "width": 150,
+ "height": 81.6
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 59.2
+ },
+ {
+ "width": 150,
+ "height": 48
+ },
+ {
+ "width": 150,
+ "height": 38.4
+ },
+ {
+ "width": 150,
+ "height": 30
+ },
+ {
+ "width": 150,
+ "height": 22.8
+ },
+ {
+ "width": 150,
+ "height": 17.2
+ },
+ {
+ "width": 150,
+ "height": 12.4
+ },
+ {
+ "width": 150,
+ "height": 9.2
+ },
+ {
+ "width": 150,
+ "height": 6.4
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 2.8
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 1.2
+ },
+ {
+ "width": 150,
+ "height": 0.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0066464543,
+ 0.059778452,
+ 0.1875459,
+ 0.39009166,
+ 0.5686131,
+ 0.70664865,
+ 0.8060679,
+ 0.87451804,
+ 0.92018366,
+ 0.94994,
+ 0.9689752,
+ 0.9809703,
+ 0.98843443,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.98828065,
+ 0.9288363,
+ 0.7806658,
+ 0.57941735,
+ 0.40687433,
+ 0.27529213,
+ 0.18131107,
+ 0.11697123,
+ 0.074225225,
+ 0.046460062,
+ 0.028744182,
+ 0.017604083,
+ 0.010684598
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 000000000000..afa005ac421e
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,304 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 234.8
+ },
+ {
+ "width": 150,
+ "height": 185.2
+ },
+ {
+ "width": 150,
+ "height": 138.8
+ },
+ {
+ "width": 150,
+ "height": 100.4
+ },
+ {
+ "width": 150,
+ "height": 66.8
+ },
+ {
+ "width": 150,
+ "height": 41.6
+ },
+ {
+ "width": 150,
+ "height": 23.6
+ },
+ {
+ "width": 150,
+ "height": 12
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9762947,
+ 0.8118515,
+ 0.60931784,
+ 0.43090785,
+ 0.29299664,
+ 0.19368339,
+ 0.12531388,
+ 0.079705715,
+ 0.049988627,
+ 0.030979574,
+ 0.019001365,
+ 0.011548042,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 000000000000..317d4804fa52
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,254 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 5.2
+ },
+ {
+ "width": 150,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 28.4
+ },
+ {
+ "width": 150,
+ "height": 63.6
+ },
+ {
+ "width": 150,
+ "height": 116.4
+ },
+ {
+ "width": 150,
+ "height": 161.2
+ },
+ {
+ "width": 150,
+ "height": 197.2
+ },
+ {
+ "width": 150,
+ "height": 225.2
+ },
+ {
+ "width": 150,
+ "height": 246.8
+ },
+ {
+ "width": 150,
+ "height": 262.4
+ },
+ {
+ "width": 150,
+ "height": 274
+ },
+ {
+ "width": 150,
+ "height": 282.4
+ },
+ {
+ "width": 150,
+ "height": 288.4
+ },
+ {
+ "width": 150,
+ "height": 292.4
+ },
+ {
+ "width": 150,
+ "height": 294.8
+ },
+ {
+ "width": 150,
+ "height": 296.4
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 298.4
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0951103,
+ 0.2911651,
+ 0.48551244,
+ 0.6439433,
+ 0.76157355,
+ 0.8441935,
+ 0.9001033,
+ 0.9369305,
+ 0.96069145,
+ 0.97577035,
+ 0.98520935,
+ 0.9910494,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
deleted file mode 100644
index 57f67665242c..000000000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
+++ /dev/null
@@ -1,654 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816,
- 832,
- 848,
- 864,
- 880,
- 896,
- 912,
- 928,
- 944,
- 960,
- 976,
- 992
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50.4
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 52.4,
- "y": 50
- },
- {
- "x": 56,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 60.8,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 393.2
- },
- {
- "width": 188,
- "height": 393.2
- },
- {
- "width": 188,
- "height": 386
- },
- {
- "width": 188,
- "height": 379.6
- },
- {
- "width": 188,
- "height": 372.8
- },
- {
- "width": 188,
- "height": 366.8
- },
- {
- "width": 188,
- "height": 360.4
- },
- {
- "width": 188,
- "height": 354
- },
- {
- "width": 188,
- "height": 347.6
- },
- {
- "width": 188,
- "height": 341.2
- },
- {
- "width": 188,
- "height": 341.2
- },
- {
- "width": 188,
- "height": 334
- },
- {
- "width": 188,
- "height": 328
- },
- {
- "width": 188,
- "height": 321.6
- },
- {
- "width": 188,
- "height": 315.2
- },
- {
- "width": 188,
- "height": 308.8
- },
- {
- "width": 188,
- "height": 302.4
- },
- {
- "width": 188,
- "height": 296
- },
- {
- "width": 188,
- "height": 289.6
- },
- {
- "width": 188,
- "height": 289.6
- },
- {
- "width": 188,
- "height": 282.8
- },
- {
- "width": 188,
- "height": 276.4
- },
- {
- "width": 188,
- "height": 270
- },
- {
- "width": 188,
- "height": 263.6
- },
- {
- "width": 188,
- "height": 257.2
- },
- {
- "width": 188,
- "height": 250.8
- },
- {
- "width": 188,
- "height": 244.4
- },
- {
- "width": 188,
- "height": 238
- },
- {
- "width": 188,
- "height": 238
- },
- {
- "width": 188,
- "height": 231.2
- },
- {
- "width": 188,
- "height": 224.8
- },
- {
- "width": 188,
- "height": 218.4
- },
- {
- "width": 188,
- "height": 212
- },
- {
- "width": 188,
- "height": 212
- },
- {
- "width": 188,
- "height": 192.4
- },
- {
- "width": 188,
- "height": 159.6
- },
- {
- "width": 188,
- "height": 124.4
- },
- {
- "width": 188,
- "height": 92.8
- },
- {
- "width": 183.2,
- "height": 66.4
- },
- {
- "width": 176,
- "height": 46
- },
- {
- "width": 170.4,
- "height": 28.8
- },
- {
- "width": 166.8,
- "height": 15.2
- },
- {
- "width": 164,
- "height": 6.4
- },
- {
- "width": 162.4,
- "height": 0.8
- },
- {
- "width": 161.2,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9808927,
- 0.8211168,
- 0.61845565,
- 0.43834114,
- 0.29850912,
- 0.19755232,
- 0.12793064,
- 0.08142871,
- 0.051099956,
- 0.031684637,
- 0.019442618,
- 0.011821032,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
deleted file mode 100644
index 01bc852cf7f4..000000000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
+++ /dev/null
@@ -1,644 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816,
- 832,
- 848,
- 864,
- 880,
- 896,
- 912,
- 928,
- 944,
- 960,
- 976
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50.8,
- "y": 52
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 52.4,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 58.4,
- "y": 50
- },
- {
- "x": 60.4,
- "y": 50
- },
- {
- "x": 61.6,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.2,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 390
- },
- {
- "width": 188,
- "height": 390
- },
- {
- "width": 188,
- "height": 379.2
- },
- {
- "width": 188,
- "height": 371.2
- },
- {
- "width": 188,
- "height": 363.2
- },
- {
- "width": 188,
- "height": 355.2
- },
- {
- "width": 188,
- "height": 347.2
- },
- {
- "width": 188,
- "height": 339.2
- },
- {
- "width": 188,
- "height": 331.2
- },
- {
- "width": 188,
- "height": 323.2
- },
- {
- "width": 188,
- "height": 323.2
- },
- {
- "width": 188,
- "height": 314.8
- },
- {
- "width": 188,
- "height": 306.8
- },
- {
- "width": 188,
- "height": 298.8
- },
- {
- "width": 188,
- "height": 290.8
- },
- {
- "width": 188,
- "height": 282.8
- },
- {
- "width": 188,
- "height": 274.8
- },
- {
- "width": 188,
- "height": 266.8
- },
- {
- "width": 188,
- "height": 258.8
- },
- {
- "width": 188,
- "height": 258.8
- },
- {
- "width": 188,
- "height": 250.4
- },
- {
- "width": 188,
- "height": 242.4
- },
- {
- "width": 188,
- "height": 234.4
- },
- {
- "width": 188,
- "height": 226.4
- },
- {
- "width": 188,
- "height": 218.4
- },
- {
- "width": 188,
- "height": 210.4
- },
- {
- "width": 188,
- "height": 202.4
- },
- {
- "width": 188,
- "height": 194.4
- },
- {
- "width": 188,
- "height": 194.4
- },
- {
- "width": 188,
- "height": 185.6
- },
- {
- "width": 188,
- "height": 178
- },
- {
- "width": 188,
- "height": 170
- },
- {
- "width": 188,
- "height": 161.6
- },
- {
- "width": 188,
- "height": 161.6
- },
- {
- "width": 188,
- "height": 144.8
- },
- {
- "width": 188,
- "height": 118.8
- },
- {
- "width": 188,
- "height": 92
- },
- {
- "width": 183.6,
- "height": 68
- },
- {
- "width": 176.8,
- "height": 48.4
- },
- {
- "width": 171.6,
- "height": 32
- },
- {
- "width": 167.6,
- "height": 18
- },
- {
- "width": 164.8,
- "height": 8.8
- },
- {
- "width": 162.8,
- "height": 2.8
- },
- {
- "width": 161.6,
- "height": 0
- },
- {
- "width": 160.8,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9967737,
- 0.86538374,
- 0.66414475,
- 0.47619528,
- 0.32686388,
- 0.21757984,
- 0.14153665,
- 0.09041709,
- 0.05691254,
- 0.035380244,
- 0.02175957,
- 0.01325649,
- 0.008007765,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
deleted file mode 100644
index a82db346ed58..000000000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
+++ /dev/null
@@ -1,444 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50.4,
- "y": 50.8
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 51.2,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 60.8,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 63.2,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 389.6
- },
- {
- "width": 188,
- "height": 378.8
- },
- {
- "width": 188,
- "height": 366
- },
- {
- "width": 188,
- "height": 352
- },
- {
- "width": 188,
- "height": 352
- },
- {
- "width": 188,
- "height": 316.8
- },
- {
- "width": 188,
- "height": 261.2
- },
- {
- "width": 188,
- "height": 202.8
- },
- {
- "width": 188,
- "height": 150.8
- },
- {
- "width": 188,
- "height": 107.6
- },
- {
- "width": 186,
- "height": 74.4
- },
- {
- "width": 177.2,
- "height": 49.6
- },
- {
- "width": 170.8,
- "height": 29.6
- },
- {
- "width": 166.8,
- "height": 12.8
- },
- {
- "width": 164,
- "height": 2.4
- },
- {
- "width": 162,
- "height": 0
- },
- {
- "width": 160.8,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9833227,
- 0.8263634,
- 0.623688,
- 0.44261706,
- 0.3016883,
- 0.1997872,
- 0.12944388,
- 0.08242595,
- 0.051743627,
- 0.032093227,
- 0.019698441,
- 0.0119793415,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
deleted file mode 100644
index 1030455e873f..000000000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
+++ /dev/null
@@ -1,324 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 53.2,
- "y": 50
- },
- {
- "x": 57.2,
- "y": 50
- },
- {
- "x": 59.6,
- "y": 50
- },
- {
- "x": 61.6,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 372
- },
- {
- "width": 188,
- "height": 312.8
- },
- {
- "width": 188,
- "height": 246.8
- },
- {
- "width": 188,
- "height": 185.2
- },
- {
- "width": 188,
- "height": 133.6
- },
- {
- "width": 188,
- "height": 93.2
- },
- {
- "width": 181.6,
- "height": 62.8
- },
- {
- "width": 174,
- "height": 40.8
- },
- {
- "width": 168.8,
- "height": 22.4
- },
- {
- "width": 165.2,
- "height": 10
- },
- {
- "width": 162.8,
- "height": 2.4
- },
- {
- "width": 161.2,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.91758585,
- 0.72435355,
- 0.52812576,
- 0.3665868,
- 0.24600428,
- 0.16102076,
- 0.103373945,
- 0.06533456,
- 0.04075712,
- 0.025142312,
- 0.015358448,
- 0.0092999935,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
deleted file mode 100644
index 622c29eebfb4..000000000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
+++ /dev/null
@@ -1,244 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 59.2,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 169.6,
- "height": 6.8
- },
- {
- "width": 188,
- "height": 26.8
- },
- {
- "width": 188,
- "height": 95.6
- },
- {
- "width": 188,
- "height": 163.2
- },
- {
- "width": 188,
- "height": 222
- },
- {
- "width": 188,
- "height": 269.6
- },
- {
- "width": 188,
- "height": 307.2
- },
- {
- "width": 188,
- "height": 335.2
- },
- {
- "width": 188,
- "height": 356
- },
- {
- "width": 188,
- "height": 370.4
- },
- {
- "width": 188,
- "height": 380.8
- },
- {
- "width": 188,
- "height": 387.6
- },
- {
- "width": 188,
- "height": 392.4
- },
- {
- "width": 188,
- "height": 395.2
- },
- {
- "width": 188,
- "height": 397.2
- },
- {
- "width": 188,
- "height": 398
- },
- {
- "width": 188,
- "height": 398.8
- },
- {
- "width": 188,
- "height": 399.2
- },
- {
- "width": 188,
- "height": 399.2
- },
- {
- "width": 188,
- "height": 399.6
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- {
- "type": "not_found"
- },
- 0,
- 0.05698657,
- 0.24197984,
- 0.44158113,
- 0.6097554,
- 0.73685503,
- 0.8271309,
- 0.8886989,
- 0.9294886,
- 0.9559254,
- 0.97276413,
- 0.98333716,
- 0.98989624,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
index f4e2328f16ab..1bc83e0401bf 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
@@ -37,7 +37,6 @@ import androidx.compose.ui.test.swipeUp
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.FeatureCaptures.elementAlpha
@@ -47,8 +46,8 @@ import com.android.compose.animation.scene.SceneTransitionLayoutForTesting
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.featureOfElement
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import kotlin.math.sin
import kotlinx.coroutines.CoroutineScope
import org.junit.Rule
@@ -63,26 +62,48 @@ import platform.test.motion.compose.createFixedConfigurationComposeMotionTestRul
import platform.test.motion.compose.recordMotion
import platform.test.motion.compose.runTest
import platform.test.motion.testing.createGoldenPathManager
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.PathElementNoContext
-@RunWith(AndroidJUnit4::class)
@MotionTest
-class ContentRevealTest {
+@RunWith(ParameterizedAndroidJunit4::class)
+class ContentRevealTest(private val isFloating: Boolean) {
+
+ private val pathConfig =
+ PathConfig(
+ PathElementNoContext("floating", isDir = false) {
+ if (isFloating) "floating" else "edge"
+ }
+ )
private val goldenPaths =
- createGoldenPathManager("frameworks/base/packages/SystemUI/compose/scene/tests/goldens")
+ createGoldenPathManager(
+ "frameworks/base/packages/SystemUI/compose/scene/tests/goldens",
+ pathConfig,
+ )
@get:Rule val motionRule = createFixedConfigurationComposeMotionTestRule(goldenPaths)
private val fakeHaptics = FakeHaptics()
+ private val motionSpec = VerticalExpandContainerSpec(isFloating)
+
@Test
fun verticalReveal_triggeredRevealOpenTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneClosed, SceneOpen))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneClosed, SceneOpen),
+ "verticalReveal_triggeredRevealOpenTransition",
+ )
}
@Test
fun verticalReveal_triggeredRevealCloseTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneOpen, SceneClosed))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneOpen, SceneClosed),
+ "verticalReveal_triggeredRevealCloseTransition",
+ )
}
@Test
@@ -90,15 +111,18 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneClosed) {
val gestureDurationMillis = 1000L
+ // detach position for the floating container is larger
+ val gestureHeight = if (isFloating) 160.dp.toPx() else 100.dp.toPx()
swipe(
curve = {
val progress = it / gestureDurationMillis.toFloat()
- val y = sin(progress * Math.PI).toFloat() * 100.dp.toPx()
+ val y = sin(progress * Math.PI).toFloat() * gestureHeight
Offset(centerX, y)
},
gestureDurationMillis,
)
- }
+ },
+ "verticalReveal_gesture_magneticDetachAndReattach",
)
}
@@ -107,7 +131,8 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneClosed) {
swipeDown(endY = 200.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragOpen",
)
}
@@ -117,7 +142,8 @@ class ContentRevealTest {
GestureRevealMotion(SceneClosed) {
val end = Offset(centerX, 80.dp.toPx())
swipeWithVelocity(start = topCenter, end = end, endVelocity = FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingOpen",
)
}
@@ -126,7 +152,8 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
swipeUp(200.dp.toPx(), 0.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragFullyClose",
)
}
@@ -134,8 +161,9 @@ class ContentRevealTest {
fun verticalReveal_gesture_dragHalfClose() {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
- swipeUp(350.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
- }
+ swipeUp(250.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
+ },
+ "verticalReveal_gesture_dragHalfClose",
)
}
@@ -146,7 +174,8 @@ class ContentRevealTest {
val start = Offset(centerX, 260.dp.toPx())
val end = Offset(centerX, 200.dp.toPx())
swipeWithVelocity(start, end, FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingClose",
)
}
@@ -164,11 +193,14 @@ class ContentRevealTest {
val gestureControl: TouchInjectionScope.() -> Unit,
) : RevealMotion
- private fun assertVerticalContainerRevealMotion(testInstructions: RevealMotion) =
+ private fun assertVerticalContainerRevealMotion(
+ testInstructions: RevealMotion,
+ goldenName: String,
+ ) =
motionRule.runTest {
val transitions = transitions {
from(SceneClosed, to = SceneOpen) {
- verticalContainerReveal(RevealElement, MotionSpec, fakeHaptics)
+ verticalContainerReveal(RevealElement, motionSpec, fakeHaptics)
}
}
@@ -221,9 +253,9 @@ class ContentRevealTest {
SceneTransitionLayoutForTesting(
state,
modifier =
- Modifier.padding(50.dp)
+ Modifier.padding(5.dp)
.background(Color.Yellow)
- .size(ContainerSize.width, ContainerSize.height + 200.dp)
+ .size(ContainerSize.width, ContainerSize.height + 100.dp)
.testTag("stl"),
) {
scene(
@@ -241,7 +273,7 @@ class ContentRevealTest {
recordingSpec,
)
- assertThat(motion).timeSeriesMatchesGolden()
+ assertThat(motion).timeSeriesMatchesGolden(goldenName)
}
@Composable
@@ -256,7 +288,7 @@ class ContentRevealTest {
modifier =
Modifier.element(RevealElement)
.size(ContainerSize)
- .edgeContainerExpansionBackground(Color.DarkGray, MotionSpec)
+ .verticalExpandContainerBackground(Color.DarkGray, motionSpec)
)
}
}
@@ -266,7 +298,9 @@ class ContentRevealTest {
}
companion object {
- val ContainerSize = DpSize(200.dp, 400.dp)
+ @get:Parameters @JvmStatic val parameterValues = listOf(true, false)
+
+ val ContainerSize = DpSize(150.dp, 300.dp)
val FlingVelocity = 1000.dp // dp/sec
@@ -274,6 +308,5 @@ class ContentRevealTest {
val SceneOpen = SceneKey("SceneB")
val RevealElement = ElementKey("RevealElement")
- val MotionSpec = EdgeContainerExpansionSpec()
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 6e29e6932629..2f819d183bcc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -34,6 +34,7 @@ import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
+import com.android.systemui.animation.TextAnimatorListener
import com.android.systemui.animation.TypefaceVariantCacheImpl
import com.android.systemui.customization.R
import com.android.systemui.log.core.LogLevel
@@ -100,7 +101,13 @@ constructor(
@VisibleForTesting
var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
val cache = TypefaceVariantCacheImpl(layout.paint.typeface, NUM_CLOCK_FONT_ANIMATION_STEPS)
- TextAnimator(layout, cache, invalidateCb)
+ TextAnimator(
+ layout,
+ cache,
+ object : TextAnimatorListener {
+ override fun onInvalidate() = invalidateCb()
+ },
+ )
}
// Used by screenshot tests to provide stability
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 37acbe261f76..5f71b19fbc3f 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
@@ -22,10 +22,10 @@ import com.android.app.animation.Interpolators
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
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.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
@@ -111,7 +111,7 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
view.updateAxes(axes)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index bc4bdf4243cb..3cfa78d17fe7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -26,6 +26,7 @@ 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.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEventListener
@@ -33,7 +34,6 @@ 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.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
@@ -232,7 +232,7 @@ class DefaultClockController(
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {}
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {}
}
open inner class DefaultClockAnimations(
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 c3935e68ca04..d778bc04ab8e 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
@@ -20,6 +20,8 @@ import android.os.Vibrator
import android.view.LayoutInflater
import com.android.systemui.customization.R
import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AxisPresetConfig
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
import com.android.systemui.plugins.clocks.ClockLogger
@@ -28,7 +30,7 @@ import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockPickerConfig
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.shared.clocks.FlexClockController.Companion.AXIS_PRESETS
+import com.android.systemui.shared.clocks.FlexClockController.Companion.buildPresetGroup
import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
private val TAG = DefaultClockProvider::class.simpleName
@@ -80,7 +82,7 @@ class DefaultClockProvider(
return if (isClockReactiveVariantsEnabled) {
val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER)
val fontAxes = getDefaultAxes(settings).merge(settings.axes)
- val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
+ val clockSettings = settings.copy(axes = ClockAxisStyle(fontAxes))
val typefaceCache =
TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
FLEX_TYPEFACE
@@ -106,17 +108,35 @@ class DefaultClockProvider(
throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
}
- return ClockPickerConfig(
- settings.clockId ?: DEFAULT_CLOCK_ID,
- resources.getString(R.string.clock_default_name),
- resources.getString(R.string.clock_default_description),
- resources.getDrawable(R.drawable.clock_default_thumbnail, null),
- isReactiveToTone = true,
- axes =
- if (!isClockReactiveVariantsEnabled) emptyList()
- else getDefaultAxes(settings).merge(settings.axes),
- axisPresets = if (!isClockReactiveVariantsEnabled) emptyList() else AXIS_PRESETS,
- )
+ if (!isClockReactiveVariantsEnabled) {
+ return ClockPickerConfig(
+ settings.clockId ?: DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description),
+ resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+ isReactiveToTone = true,
+ axes = emptyList(),
+ presetConfig = null,
+ )
+ } else {
+ val fontAxes = getDefaultAxes(settings).merge(settings.axes)
+ return ClockPickerConfig(
+ settings.clockId ?: DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description),
+ resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+ isReactiveToTone = true,
+ axes = fontAxes,
+ presetConfig =
+ AxisPresetConfig(
+ listOf(
+ buildPresetGroup(resources, isRound = true),
+ buildPresetGroup(resources, isRound = false),
+ )
+ )
+ .let { cfg -> cfg.copy(current = cfg.findStyle(ClockAxisStyle(fontAxes))) },
+ )
+ }
}
companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 5acd4468fe92..96c3ac75587e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,20 +16,24 @@
package com.android.systemui.shared.clocks
+import android.content.res.Resources
import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.AxisPresetConfig
import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEventListener
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.put
+import com.android.systemui.shared.clocks.FontUtils.toClockAxis
import com.android.systemui.shared.clocks.view.FlexClockView
import java.io.PrintWriter
import java.util.Locale
@@ -96,8 +100,8 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
largeClock.events.onZenDataChanged(data)
}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
- val fontAxes = getDefaultAxes(clockCtx.settings).merge(axes).map { it.toSetting() }
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
+ val fontAxes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(axes))
smallClock.events.onFontAxesChanged(fontAxes)
largeClock.events.onFontAxesChanged(fontAxes)
}
@@ -162,66 +166,46 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
),
)
- private val LEGACY_FLEX_SETTINGS =
- listOf(
- GSFAxes.WEIGHT.toClockAxisSetting(600f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- )
+ private val LEGACY_FLEX_SETTINGS = ClockAxisStyle {
+ put(GSFAxes.WEIGHT, 600f)
+ put(GSFAxes.WIDTH, 100f)
+ put(GSFAxes.ROUND, 100f)
+ put(GSFAxes.SLANT, 0f)
+ }
- val AXIS_PRESETS =
- listOf(
- FONT_AXES.map { it.toSetting() },
- LEGACY_FLEX_SETTINGS,
- listOf( // Porcelain
- GSFAxes.WEIGHT.toClockAxisSetting(500f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Midnight
- GSFAxes.WEIGHT.toClockAxisSetting(300f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-10f),
- ),
- listOf( // Sterling
- GSFAxes.WEIGHT.toClockAxisSetting(1000f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Smoky Green
- GSFAxes.WEIGHT.toClockAxisSetting(150f),
- GSFAxes.WIDTH.toClockAxisSetting(50f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Iris
- GSFAxes.WEIGHT.toClockAxisSetting(500f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Margarita
- GSFAxes.WEIGHT.toClockAxisSetting(300f),
- GSFAxes.WIDTH.toClockAxisSetting(30f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-10f),
- ),
- listOf( // Raspberry
- GSFAxes.WEIGHT.toClockAxisSetting(700f),
- GSFAxes.WIDTH.toClockAxisSetting(140f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-7f),
- ),
- listOf( // Ultra Blue
- GSFAxes.WEIGHT.toClockAxisSetting(850f),
- GSFAxes.WIDTH.toClockAxisSetting(130f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
+ private val PRESET_COUNT = 8
+ private val PRESET_WIDTH_INIT = 30f
+ private val PRESET_WIDTH_STEP = 12.5f
+ private val PRESET_WEIGHT_INIT = 800f
+ private val PRESET_WEIGHT_STEP = -100f
+ private val BASE_PRESETS: List<ClockAxisStyle> = run {
+ val presets = mutableListOf<ClockAxisStyle>()
+ var weight = PRESET_WEIGHT_INIT
+ var width = PRESET_WIDTH_INIT
+ for (i in 1..PRESET_COUNT) {
+ presets.add(
+ ClockAxisStyle {
+ put(GSFAxes.WEIGHT, weight)
+ put(GSFAxes.WIDTH, width)
+ put(GSFAxes.ROUND, 0f)
+ put(GSFAxes.SLANT, 0f)
+ }
+ )
+
+ weight += PRESET_WEIGHT_STEP
+ width += PRESET_WIDTH_STEP
+ }
+
+ return@run presets
+ }
+
+ fun buildPresetGroup(resources: Resources, isRound: Boolean): AxisPresetConfig.Group {
+ val round = if (isRound) GSFAxes.ROUND.maxValue else GSFAxes.ROUND.minValue
+ return AxisPresetConfig.Group(
+ presets = BASE_PRESETS.map { it.copy { put(GSFAxes.ROUND, round) } },
+ // TODO(b/395647577): Placeholder Icon; Replace or remove
+ icon = resources.getDrawable(R.drawable.clock_default_thumbnail, null),
)
+ }
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 578a489c68c6..171a68f72e20 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -25,16 +25,18 @@ import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
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.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.get
+import com.android.systemui.shared.clocks.FontUtils.set
import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
@@ -129,17 +131,10 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
layerController.faceEvents.onThemeChanged(theme)
}
- override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
- var axes = settings
- if (!isLargeClock) {
- axes =
- axes.map { axis ->
- if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
- axis.copy(value = SMALL_CLOCK_MAX_WDTH)
- } else {
- axis
- }
- }
+ override fun onFontAxesChanged(settings: ClockAxisStyle) {
+ var axes = ClockAxisStyle(settings)
+ if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
+ axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
}
layerController.events.onFontAxesChanged(axes)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
index 212b1e29d1b8..722d76beedbb 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -18,26 +18,36 @@ package com.android.systemui.shared.clocks
import com.android.systemui.animation.AxisDefinition
import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockFontAxis
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-fun AxisDefinition.toClockAxis(
- type: AxisType,
- currentValue: Float? = null,
- name: String,
- description: String,
-): ClockFontAxis {
- return ClockFontAxis(
- key = this.tag,
- type = type,
- maxValue = this.maxValue,
- minValue = this.minValue,
- currentValue = currentValue ?: this.defaultValue,
- name = name,
- description = description,
- )
-}
+object FontUtils {
+ fun AxisDefinition.toClockAxis(
+ type: AxisType,
+ currentValue: Float? = null,
+ name: String,
+ description: String,
+ ): ClockFontAxis {
+ return ClockFontAxis(
+ key = this.tag,
+ type = type,
+ maxValue = this.maxValue,
+ minValue = this.minValue,
+ currentValue = currentValue ?: this.defaultValue,
+ name = name,
+ description = description,
+ )
+ }
+
+ fun ClockAxisStyle.put(def: AxisDefinition, value: Float? = null) {
+ this.put(def.tag, value ?: def.defaultValue)
+ }
+
+ operator fun ClockAxisStyle.set(def: AxisDefinition, value: Float) {
+ this[def.tag] = value
+ }
-fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
- return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+ operator fun ClockAxisStyle.get(def: AxisDefinition): Float {
+ return this[def.tag] ?: def.defaultValue
+ }
}
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 1d963af3ad22..7be9a936cbd3 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
@@ -26,10 +26,10 @@ import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
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.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
@@ -172,7 +172,7 @@ open class SimpleDigitalHandLayerController(
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
view.updateAxes(axes)
}
}
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 ba32ab083063..4531aed0e83d 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
@@ -26,7 +26,7 @@ import androidx.annotation.VisibleForTesting
import androidx.core.view.children
import com.android.app.animation.Interpolators
import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockLogger
import com.android.systemui.plugins.clocks.VPoint
import com.android.systemui.plugins.clocks.VPointF
@@ -272,7 +272,7 @@ class FlexClockView(clockCtx: ClockContext) : ViewGroup(clockCtx.context) {
invalidate()
}
- fun updateAxes(axes: List<ClockFontAxisSetting>) {
+ fun updateAxes(axes: ClockAxisStyle) {
childViews.forEach { view -> view.updateAxes(axes) }
requestLayout()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 2af25fe339a2..377a24c2899b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -24,7 +24,6 @@ import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.os.VibrationEffect
-import android.text.Layout
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
@@ -37,12 +36,12 @@ import android.view.animation.PathInterpolator
import android.widget.TextView
import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.AxisDefinition
import com.android.systemui.animation.GSFAxes
import com.android.systemui.animation.TextAnimator
+import com.android.systemui.animation.TextAnimatorListener
import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockLogger
import com.android.systemui.plugins.clocks.VPoint
import com.android.systemui.plugins.clocks.VPointF
@@ -56,9 +55,9 @@ import com.android.systemui.shared.clocks.DigitTranslateAnimator
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
import com.android.systemui.shared.clocks.FontTextStyle
+import com.android.systemui.shared.clocks.FontUtils.set
import com.android.systemui.shared.clocks.ViewUtils.measuredSize
import com.android.systemui.shared.clocks.ViewUtils.size
-import com.android.systemui.shared.clocks.toClockAxisSetting
import java.lang.Thread
import kotlin.math.max
import kotlin.math.min
@@ -123,9 +122,9 @@ open class SimpleDigitalClockTextView(
private val isLegacyFlex = clockCtx.settings.clockId == FLEX_CLOCK_ID
private val fixedAodAxes =
when {
- !isLegacyFlex -> listOf(AOD_WEIGHT_AXIS, WIDTH_AXIS)
- isLargeClock -> listOf(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
- else -> listOf(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+ !isLegacyFlex -> fromAxes(AOD_WEIGHT_AXIS, WIDTH_AXIS)
+ isLargeClock -> fromAxes(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+ else -> fromAxes(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
}
private var lsFontVariation: String
@@ -135,11 +134,11 @@ open class SimpleDigitalClockTextView(
init {
val roundAxis = if (!isLegacyFlex) ROUND_AXIS else FLEX_ROUND_AXIS
val lsFontAxes =
- if (!isLegacyFlex) listOf(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
- else listOf(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
+ if (!isLegacyFlex) fromAxes(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
+ else fromAxes(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
lsFontVariation = lsFontAxes.toFVar()
- aodFontVariation = (fixedAodAxes + listOf(roundAxis, SLANT_AXIS)).toFVar()
+ aodFontVariation = fixedAodAxes.copyWith(fromAxes(roundAxis, SLANT_AXIS)).toFVar()
fidgetFontVariation = buildFidgetVariation(lsFontAxes).toFVar()
}
@@ -175,11 +174,6 @@ open class SimpleDigitalClockTextView(
private val typefaceCache = clockCtx.typefaceCache.getVariantCache("")
- @VisibleForTesting
- var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
- TextAnimator(layout, typefaceCache, invalidateCb)
- }
-
var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.CENTER
@@ -206,9 +200,9 @@ open class SimpleDigitalClockTextView(
invalidate()
}
- fun updateAxes(lsAxes: List<ClockFontAxisSetting>) {
+ fun updateAxes(lsAxes: ClockAxisStyle) {
lsFontVariation = lsAxes.toFVar()
- aodFontVariation = lsAxes.replace(fixedAodAxes).toFVar()
+ aodFontVariation = lsAxes.copyWith(fixedAodAxes).toFVar()
fidgetFontVariation = buildFidgetVariation(lsAxes).toFVar()
logger.updateAxes(lsFontVariation, aodFontVariation)
@@ -225,19 +219,16 @@ open class SimpleDigitalClockTextView(
invalidate()
}
- fun buildFidgetVariation(axes: List<ClockFontAxisSetting>): List<ClockFontAxisSetting> {
- val result = mutableListOf<ClockFontAxisSetting>()
- for (axis in axes) {
- result.add(
- FIDGET_DISTS.get(axis.key)?.let { (dist, midpoint) ->
- ClockFontAxisSetting(
- axis.key,
- axis.value + dist * if (axis.value > midpoint) -1 else 1,
- )
- } ?: axis
- )
- }
- return result
+ fun buildFidgetVariation(axes: ClockAxisStyle): ClockAxisStyle {
+ return ClockAxisStyle(
+ axes.items
+ .map { (key, value) ->
+ FIDGET_DISTS.get(key)?.let { (dist, midpoint) ->
+ key to value + dist * if (value > midpoint) -1 else 1
+ } ?: (key to value)
+ }
+ .toMap()
+ )
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -247,7 +238,18 @@ open class SimpleDigitalClockTextView(
val layout = this.layout
if (layout != null) {
if (!this::textAnimator.isInitialized) {
- textAnimator = textAnimatorFactory(layout, ::invalidate)
+ textAnimator =
+ TextAnimator(
+ layout,
+ typefaceCache,
+ object : TextAnimatorListener {
+ override fun onInvalidate() = invalidate()
+
+ override fun onRebased() = updateTextBounds()
+
+ override fun onPaintModified() = updateTextBounds()
+ },
+ )
setInterpolatorPaint()
} else {
textAnimator.updateLayout(layout)
@@ -272,7 +274,7 @@ open class SimpleDigitalClockTextView(
override fun onDraw(canvas: Canvas) {
logger.onDraw(textAnimator.textInterpolator.shapedText)
- val interpProgress = getInterpolatedProgress()
+ val interpProgress = textAnimator.progress
val interpBounds = getInterpolatedTextBounds(interpProgress)
if (interpProgress != drawnProgress) {
drawnProgress = interpProgress
@@ -336,7 +338,6 @@ open class SimpleDigitalClockTextView(
interpolator = aodDozingInterpolator,
),
)
- updateTextBoundsForTextAnimator()
if (!isAnimated) {
requestLayout()
@@ -367,11 +368,9 @@ open class SimpleDigitalClockTextView(
duration = CHARGE_ANIMATION_DURATION,
),
)
- updateTextBoundsForTextAnimator()
},
),
)
- updateTextBoundsForTextAnimator()
}
fun animateFidget(x: Float, y: Float) = animateFidget(0L)
@@ -401,11 +400,9 @@ open class SimpleDigitalClockTextView(
interpolator = FIDGET_INTERPOLATOR,
),
)
- updateTextBoundsForTextAnimator()
},
),
)
- updateTextBoundsForTextAnimator()
}
fun refreshText() {
@@ -428,12 +425,8 @@ open class SimpleDigitalClockTextView(
id == R.id.MINUTE_SECOND_DIGIT
}
- private fun getInterpolatedProgress(): Float {
- return textAnimator.animator?.let { it.animatedValue as Float } ?: 1f
- }
-
/** Returns the interpolated text bounding rect based on interpolation progress */
- private fun getInterpolatedTextBounds(progress: Float = getInterpolatedProgress()): VRectF {
+ private fun getInterpolatedTextBounds(progress: Float = textAnimator.progress): VRectF {
if (progress <= 0f) {
return prevTextBounds
} else if (!textAnimator.isRunning || progress >= 1f) {
@@ -487,6 +480,15 @@ open class SimpleDigitalClockTextView(
MeasureSpec.makeMeasureSpec(measureBounds.x.roundToInt(), mode.x),
MeasureSpec.makeMeasureSpec(measureBounds.y.roundToInt(), mode.y),
)
+
+ logger.d({
+ val size = VPointF.fromLong(long1)
+ val mode = VPoint.fromLong(long2)
+ "setInterpolatedSize(size=$size, mode=$mode)"
+ }) {
+ long1 = measureBounds.toLong()
+ long2 = mode.toLong()
+ }
}
/** Set the location of the view to match the interpolated text bounds */
@@ -514,6 +516,9 @@ open class SimpleDigitalClockTextView(
targetRect.bottom.roundToInt(),
)
onViewBoundsChanged?.let { it(targetRect) }
+ logger.d({ "setInterpolatedLocation(${VRectF.fromLong(long1)})" }) {
+ long1 = targetRect.toLong()
+ }
return targetRect
}
@@ -616,7 +621,8 @@ open class SimpleDigitalClockTextView(
* rebase if previous animator is canceled so basePaint will store the state we transition from
* and targetPaint will store the state we transition to
*/
- private fun updateTextBoundsForTextAnimator() {
+ private fun updateTextBounds() {
+ drawnProgress = null
prevTextBounds = textAnimator.textInterpolator.basePaint.getTextBounds(text)
targetTextBounds = textAnimator.textInterpolator.targetPaint.getTextBounds(text)
}
@@ -656,18 +662,22 @@ open class SimpleDigitalClockTextView(
)
val AOD_COLOR = Color.WHITE
- val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
- val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
- val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
- val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
- val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
+ private val LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 400f
+ private val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT to 200f
+ private val WIDTH_AXIS = GSFAxes.WIDTH to 85f
+ private val ROUND_AXIS = GSFAxes.ROUND to 0f
+ private val SLANT_AXIS = GSFAxes.SLANT to 0f
// Axes for Legacy version of the Flex Clock
- val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
- val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
- val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
- val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
- val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
- val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
+ private val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 600f
+ private val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT to 74f
+ private val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT to 133f
+ private val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH to 100f
+ private val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH to 43f
+ private val FLEX_ROUND_AXIS = GSFAxes.ROUND to 100f
+
+ private fun fromAxes(vararg axes: Pair<AxisDefinition, Float>): ClockAxisStyle {
+ return ClockAxisStyle(axes.map { (def, value) -> def.tag to value }.toMap())
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 14d34d79512f..162218d8f071 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -87,7 +87,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
contentResolver,
selectedUserInteractor,
lazyKeyguardUpdateMonitor,
- dumpManager
+ dumpManager,
)
}
@@ -116,9 +116,9 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
)
)
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
- )
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
)
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -212,7 +212,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -285,7 +285,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
"${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
"|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
@@ -328,7 +328,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.NONE.intValue}",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -358,7 +358,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
"|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -397,10 +397,10 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
@Test
fun isWakeupConsideredUnlockIntent_singleValue() {
// GIVEN lift is considered an unlock intent
- secureSettings.putIntForUser(
+ secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- PowerManager.WAKE_REASON_LIFT,
- currentUser
+ PowerManager.WAKE_REASON_LIFT.toString(),
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -422,7 +422,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
PowerManager.WAKE_REASON_TAP.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -452,7 +452,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
" ",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -479,7 +479,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
PowerManager.WAKE_REASON_LIFT.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -501,7 +501,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
" ",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -521,7 +521,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
PowerManager.WAKE_REASON_TAP.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -544,7 +544,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"-1",
- currentUser
+ currentUser,
)
// WHEN the setting updates
@@ -581,7 +581,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
eq(uri),
eq(false),
capture(settingsObserverCaptor),
- eq(UserHandle.USER_ALL)
+ eq(UserHandle.USER_ALL),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 245388c214a5..b0db8b70d296 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -22,12 +22,12 @@ 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
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
@@ -47,16 +47,14 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val progressProvider: FakeUnfoldTransitionProvider =
kosmos.fakeUnfoldTransitionProgressProvider
- @Mock
- private lateinit var keyguardRootView: KeyguardRootView
+ @Mock private lateinit var keyguardRootView: KeyguardRootView
- @Mock
- private lateinit var notificationShadeWindowView: NotificationShadeWindowView
+ @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -71,10 +69,14 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
xTranslationMax =
context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- underTest = KeyguardUnfoldTransition(
- context, keyguardRootView, notificationShadeWindowView,
- statusBarStateController, progressProvider
- )
+ underTest =
+ KeyguardUnfoldTransition(
+ context,
+ keyguardRootView,
+ notificationShadeWindowView,
+ statusBarStateController,
+ progressProvider,
+ )
underTest.setup()
underTest.statusViewCentered = false
@@ -88,9 +90,8 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
underTest.statusViewCentered = true
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
- view
- )
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -110,9 +111,8 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
whenever(statusBarStateController.getState()).thenReturn(SHADE)
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
- view
- )
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -133,9 +133,11 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() {
val view = View(context)
whenever(
- notificationShadeWindowView
- .findViewById<View>(customR.id.lockscreen_clock_view_large)
- ).thenReturn(view)
+ notificationShadeWindowView.findViewById<View>(
+ customR.id.lockscreen_clock_view_large
+ )
+ )
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
index cde42bd00ba5..76606230a124 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
@@ -23,7 +23,6 @@ import androidx.test.filters.SmallTest
import com.android.internal.accessibility.AccessibilityShortcutController
import com.android.internal.accessibility.common.ShortcutConstants
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -31,6 +30,7 @@ import com.android.systemui.model.SysUiState
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@ class ExtraDimDialogDelegateTest : SysuiTestCase() {
private lateinit var extraDimDialogDelegate: ExtraDimDialogDelegate
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
@Mock private lateinit var dialog: SystemUIDialog
@@ -79,7 +79,7 @@ class ExtraDimDialogDelegateTest : SysuiTestCase() {
kosmos.testDispatcher,
dialogFactory,
accessibilityManager,
- userTracker
+ userTracker,
)
}
@@ -94,7 +94,7 @@ class ExtraDimDialogDelegateTest : SysuiTestCase() {
verify(dialog)
.setPositiveButton(
eq(R.string.accessibility_deprecate_extra_dim_dialog_button),
- clickListener.capture()
+ clickListener.capture(),
)
clickListener.firstValue.onClick(dialog, 0)
@@ -110,7 +110,7 @@ class ExtraDimDialogDelegateTest : SysuiTestCase() {
.flattenToString()
)
),
- anyInt()
+ anyInt(),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 7e4704a6179a..d118ace08b85 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -141,6 +141,10 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
@Mock
private QSSettingsPackageRepository mQSSettingsPackageRepository;
@Mock
+ private HearingDevicesInputRoutingController.Factory mInputRoutingFactory;
+ @Mock
+ private HearingDevicesInputRoutingController mInputRoutingController;
+ @Mock
private CachedBluetoothDevice mCachedDevice;
@Mock
private BluetoothDevice mDevice;
@@ -184,6 +188,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+ when(mInputRoutingFactory.create(any())).thenReturn(mInputRoutingController);
mContext.setMockPackageManager(mPackageManager);
}
@@ -349,6 +354,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
+ mExecutor.runAllReady();
ViewGroup ambientLayout = getAmbientLayout(mDialog);
assertThat(ambientLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -401,7 +407,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mExecutor,
mAudioManager,
mUiEventLogger,
- mQSSettingsPackageRepository
+ mQSSettingsPackageRepository,
+ mInputRoutingFactory
);
mDialog = mDialogDelegate.createDialog();
}
@@ -438,7 +445,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
return dialog.requireViewById(R.id.ambient_layout);
}
-
private int countChildWithoutSpace(ViewGroup viewGroup) {
int spaceCount = 0;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
new file mode 100644
index 000000000000..0a41b771a335
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.media.AudioDeviceInfo
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesInputRoutingController.InputRoutingControlAvailableCallback
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesInputRoutingControllerTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private var hapClientProfile: HapClientProfile = mock()
+ private var cachedDevice: CachedBluetoothDevice = mock()
+ private var memberCachedDevice: CachedBluetoothDevice = mock()
+ private var btDevice: android.bluetooth.BluetoothDevice = mock()
+ private var audioManager: AudioManager = mock()
+ private lateinit var underTest: HearingDevicesInputRoutingController
+ private val testDispatcher = kosmos.testDispatcher
+
+ @Before
+ fun setUp() {
+ hapClientProfile.stub { on { isProfileReady } doReturn true }
+ cachedDevice.stub {
+ on { device } doReturn btDevice
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ memberCachedDevice.stub {
+ on { device } doReturn btDevice
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+
+ underTest = HearingDevicesInputRoutingController(mContext, audioManager, testDispatcher)
+ underTest.setDevice(cachedDevice)
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_validInput_supportHapProfile_returnTrue() {
+ testScope.runTest {
+ val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isTrue()
+ }
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_notSupportHapProfile_returnFalse() {
+ testScope.runTest {
+ val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn emptyList()
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isFalse()
+ }
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_validInputMember_supportHapProfile_returnTrue() {
+ testScope.runTest {
+ val mockInfoAddress2 = arrayOf(mockTestAddressInfo(TEST_ADDRESS_2))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ on { memberDevice } doReturn (setOf(memberCachedDevice))
+ }
+ memberCachedDevice.stub { on { address } doReturn TEST_ADDRESS_2 }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress2
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isTrue()
+ }
+ }
+
+ @Test
+ fun isAvailable_notValidInputDevice_returnFalse() {
+ testScope.runTest {
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn emptyArray()
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isFalse()
+ }
+ }
+
+ @Test
+ fun selectInputRouting_builtinMic_setMicrophonePreferredForCallsFalse() {
+ underTest.selectInputRouting(
+ HearingDevicesInputRoutingController.InputRoutingValue.BUILTIN_MIC.ordinal
+ )
+
+ verify(btDevice).isMicrophonePreferredForCalls = false
+ }
+
+ private fun mockTestAddressInfo(address: String): AudioDeviceInfo {
+ val info: AudioDeviceInfo = mock()
+ info.stub {
+ on { type } doReturn AudioDeviceInfo.TYPE_BLE_HEADSET
+ on { this.address } doReturn address
+ }
+
+ return info
+ }
+
+ companion object {
+ private const val TEST_ADDRESS = "55:66:77:88:99:AA"
+ private const val TEST_ADDRESS_2 = "55:66:77:88:99:BB"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8c5fad3906ed..85733124aedb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -32,7 +32,6 @@ import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -50,6 +49,7 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -74,7 +74,7 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidJUnit4::class)
class BackActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val executor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
index 648d74d20cc5..29a0b6922b2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -22,8 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val settings = kosmos.fakeSettings
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
index b73a212c9bd1..2e357d8a8652 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
@@ -22,8 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@ import org.mockito.Mockito
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
private val privacyManager = mock<SensorPrivacyManager>()
@@ -46,7 +46,7 @@ class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
CameraSensorPrivacyRepositoryImpl(
testScope.testScheduler,
testScope.backgroundScope,
- privacyManager
+ privacyManager,
)
@Test
@@ -87,7 +87,7 @@ class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
.addSensorPrivacyListener(
ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA),
ArgumentMatchers.eq(testUser.identifier),
- captor.capture()
+ captor.capture(),
)
val sensorPrivacyCallback = captor.value!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
index 6c8097ed7166..b3d898396497 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.fakeCameraAutoRotateRepository
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
index 7161c2c13a60..6b9a7de2e781 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.fakeCameraSensorPrivacyRepository
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
index 239e02640908..652a2ff21e9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -25,6 +25,10 @@ import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -33,7 +37,11 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
- val creator = ActionIntentCreator()
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
+
+ val creator = ActionIntentCreator(testScope.backgroundScope)
@Test
fun test_getTextEditorIntent() {
@@ -65,7 +73,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
}
@Test
- fun test_getImageEditIntent() {
+ fun test_getImageEditIntent() = runTest {
context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
val fakeUri = Uri.parse("content://foo")
var intent = creator.getImageEditIntent(fakeUri, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index 126b3fa9e7ca..10c5c52d21a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -34,6 +34,8 @@ import com.android.systemui.res.R;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.atomic.AtomicReference;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DefaultIntentCreatorTest extends SysuiTestCase {
@@ -73,12 +75,16 @@ public class DefaultIntentCreatorTest extends SysuiTestCase {
}
@Test
- public void test_getImageEditIntent() {
+ public void test_getImageEditIntentAsync() {
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
"");
Uri fakeUri = Uri.parse("content://foo");
- Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
+ final AtomicReference<Intent> intentHolder = new AtomicReference<>(null);
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ Intent intent = intentHolder.get();
assertEquals(Intent.ACTION_EDIT, intent.getAction());
assertEquals("image/*", intent.getType());
assertEquals(null, intent.getComponent());
@@ -90,8 +96,10 @@ public class DefaultIntentCreatorTest extends SysuiTestCase {
"com.android.remotecopy.RemoteCopyActivity");
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
fakeComponent.flattenToString());
- intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
- assertEquals(fakeComponent, intent.getComponent());
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ assertEquals(fakeComponent, intentHolder.get().getComponent());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index df10d058c5d1..b08e6761d92f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,12 +16,14 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalEnabled
@@ -49,6 +51,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -65,6 +68,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
+ private lateinit var communalInteractorSpy: CommunalInteractor
private lateinit var underTest: CommunalAppWidgetHostStartable
@Before
@@ -78,12 +82,13 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
helper = kosmos.fakeGlanceableHubMultiUserHelper
appWidgetIdToRemove = MutableSharedFlow()
whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
+ communalInteractorSpy = spy(kosmos.communalInteractor)
underTest =
CommunalAppWidgetHostStartable(
{ appWidgetHost },
{ communalWidgetHost },
- { kosmos.communalInteractor },
+ { communalInteractorSpy },
{ kosmos.communalSettingsInteractor },
{ kosmos.keyguardInteractor },
{ kosmos.fakeUserTracker },
@@ -259,6 +264,41 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
}
@Test
+ fun removeNotLockscreenWidgets_whenCommunalIsAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is available
+ setCommunalAvailable(true)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ fakeCommunalWidgetRepository.addWidget(
+ appWidgetId = 1,
+ userId = MAIN_USER_INFO.id,
+ category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(
+ appWidgetId = 3,
+ userId = MAIN_USER_INFO.id,
+ category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+ )
+
+ underTest.start()
+ runCurrent()
+
+ val communalWidgets by
+ collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+ assertThat(communalWidgets).hasSize(1)
+ assertThat(communalWidgets!![0].appWidgetId).isEqualTo(2)
+
+ verify(communalInteractorSpy).deleteWidget(1)
+ verify(communalInteractorSpy).deleteWidget(3)
+ }
+ }
+
+ @Test
fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
with(kosmos) {
testScope.runTest {
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 197b0eea05cb..859137507bbf 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
@@ -26,6 +26,7 @@ import android.view.Display.TYPE_INTERNAL
import android.view.IWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
@@ -71,14 +72,20 @@ class DisplayRepositoryTest : SysuiTestCase() {
// that the initial state (soon after construction) contains the expected ones set in every
// test.
private val displayRepository: DisplayRepositoryImpl by lazy {
- DisplayRepositoryImpl(
+ // TODO b/401305290 - move this to kosmos
+ val displayRepositoryFromLib =
+ com.android.app.displaylib.DisplayRepositoryImpl(
displayManager,
- commandQueue,
- windowManager,
testHandler,
- TestScope(UnconfinedTestDispatcher()),
+ testScope.backgroundScope,
UnconfinedTestDispatcher(),
)
+ DisplayRepositoryImpl(
+ commandQueue,
+ windowManager,
+ testScope.backgroundScope,
+ displayRepositoryFromLib,
+ )
.also {
verify(displayManager, never()).registerDisplayListener(any(), any())
// It needs to be called, just once, for the initial value.
@@ -403,7 +410,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayChanged(1)
@@ -416,7 +423,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayConnected(2, TYPE_EXTERNAL)
@@ -648,7 +655,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
return flowValue
}
- private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+ private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> {
val flowValue = collectLastValue(displayRepository.pendingDisplay)
captureAddedRemovedListener()
verify(displayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index e41d46ce90af..6c7783ae44e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -31,7 +31,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.kotlin.eq
+import org.mockito.kotlin.any
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@@ -105,7 +105,7 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() {
@Test
fun start_registersDumpable() {
- verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+ verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
}
private fun createDisplay(displayId: Int): Display =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index f2a6c11b872e..722976131caf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -26,9 +26,9 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.education.data.model.EduDeviceConnectionTime
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.education.domain.interactor.mockEduInputManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.io.File
import javax.inject.Provider
@@ -48,7 +48,7 @@ import org.junit.runner.RunWith
class ContextualEducationRepositoryTest : SysuiTestCase() {
private lateinit var underTest: UserContextualEducationRepository
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val dsScopeProvider: Provider<CoroutineScope> = Provider {
TestScope(kosmos.testDispatcher).backgroundScope
@@ -70,7 +70,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
testContext,
dsScopeProvider,
kosmos.mockEduInputManager,
- kosmos.testDispatcher
+ kosmos.testDispatcher,
)
underTest.setUser(testUserId)
}
@@ -109,7 +109,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
lastEducationTime = kosmos.fakeEduClock.instant(),
usageSessionStartTime = kosmos.fakeEduClock.instant(),
userId = testUserId,
- gestureType = BACK
+ gestureType = BACK,
)
underTest.updateGestureEduModel(BACK) { newModel }
val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 5030d1e49da0..0f75e57ce771 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -21,9 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -45,7 +45,7 @@ import org.mockito.junit.MockitoRule
@RunWith(AndroidJUnit4::class)
class HapticSliderPluginTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
@Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
index 798c5ab817a5..39b5e81345ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
@@ -23,9 +23,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
import com.android.systemui.keyboard.data.repository.keyboardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.data.repository.userRepository
@@ -41,7 +41,7 @@ import org.junit.runner.RunWith
class UserInputDeviceRepositoryTest : SysuiTestCase() {
private lateinit var underTest: UserInputDeviceRepository
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyboardRepository = kosmos.keyboardRepository
private val touchpadRepository = kosmos.touchpadRepository
@@ -54,7 +54,7 @@ class UserInputDeviceRepositoryTest : SysuiTestCase() {
kosmos.testDispatcher,
keyboardRepository,
touchpadRepository,
- kosmos.userRepository
+ kosmos.userRepository,
)
userRepository.setUserInfos(USER_INFOS)
}
@@ -72,7 +72,7 @@ class UserInputDeviceRepositoryTest : SysuiTestCase() {
assertThat(isAnyKeyboardConnected)
.containsExactly(
UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
- UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+ UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
)
.inOrder()
}
@@ -90,16 +90,13 @@ class UserInputDeviceRepositoryTest : SysuiTestCase() {
assertThat(isAnyTouchpadConnected)
.containsExactly(
UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
- UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+ UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
)
.inOrder()
}
companion object {
private val USER_INFOS =
- listOf(
- UserInfo(100, "First User", 0),
- UserInfo(101, "Second User", 0),
- )
+ listOf(UserInfo(100, "First User", 0), UserInfo(101, "Second User", 0))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
index 274880b484cc..6bd0fb0e17a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
@@ -23,9 +23,9 @@ import android.view.inputmethod.InputMethodSubtype
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@ class InputMethodRepositoryTest : SysuiTestCase() {
@Mock private lateinit var inputMethodManager: InputMethodManager
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var underTest: InputMethodRepository
@@ -72,7 +72,7 @@ class InputMethodRepositoryTest : SysuiTestCase() {
inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
any(),
anyBoolean(),
- eq(USER_HANDLE)
+ eq(USER_HANDLE),
)
)
.thenReturn(listOf())
@@ -97,7 +97,7 @@ class InputMethodRepositoryTest : SysuiTestCase() {
inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
eq(selectedImiId),
anyBoolean(),
- eq(USER_HANDLE)
+ eq(USER_HANDLE),
)
)
.thenReturn(
@@ -125,7 +125,7 @@ class InputMethodRepositoryTest : SysuiTestCase() {
verify(inputMethodManager)
.showInputMethodPickerFromSystem(
/* showAuxiliarySubtypes = */ eq(true),
- /* displayId = */ eq(displayId)
+ /* displayId = */ eq(displayId),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
index 8e6de2f04279..9e129a43b4cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
@@ -22,8 +22,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.inputmethod.data.model.InputMethodModel
import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
import com.android.systemui.inputmethod.data.repository.inputMethodRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class InputMethodInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val fakeInputMethodRepository = kosmos.fakeInputMethodRepository
@@ -148,7 +148,7 @@ class InputMethodInteractorTest : SysuiTestCase() {
subtypes =
List(auxiliarySubtypes + nonAuxiliarySubtypes) {
InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
- }
+ },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 1bb4805d4f16..655c646cd34c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -34,7 +34,6 @@ import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcut
import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
@@ -43,6 +42,7 @@ import com.android.systemui.plugins.activityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -60,7 +60,7 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() {
private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
private val mockUserContext: Context = mock()
private val kosmos =
- Kosmos().also {
+ testKosmos().also {
it.testCase = this
it.testDispatcher = UnconfinedTestDispatcher()
it.shortcutHelperSystemShortcutsSource = fakeSystemSource
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index be9e93c64053..ba57ffd37053 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -26,7 +26,7 @@ import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
import com.android.systemui.keyboard.stickykeys.shared.model.Locked
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -52,19 +52,19 @@ class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() {
fun setup() {
val dialogFactory = mock<StickyKeyDialogFactory>()
whenever(dialogFactory.create(any())).thenReturn(dialog)
- val keyboardRepository = Kosmos().keyboardRepository
+ val keyboardRepository = testKosmos().keyboardRepository
val viewModel =
StickyKeysIndicatorViewModel(
stickyKeysRepository,
keyboardRepository,
- testScope.backgroundScope
+ testScope.backgroundScope,
)
coordinator =
StickyKeysIndicatorCoordinator(
testScope.backgroundScope,
dialogFactory,
viewModel,
- mock<StickyKeysLogger>()
+ mock<StickyKeysLogger>(),
)
coordinator.startListening()
keyboardRepository.setIsAnyKeyboardConnected(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9daf0ffd34b4..1c0041969504 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -33,7 +33,6 @@ import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
@@ -64,7 +63,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
private val inputManager = mock<InputManager>()
private val keyboardRepository = FakeKeyboardRepository()
private val secureSettings = kosmos.fakeSettings
- private val userRepository = Kosmos().fakeUserRepository
+ private val userRepository = testKosmos().fakeUserRepository
private val captor =
ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index baf3b5b4430f..3f1cadc5fe76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -23,12 +23,12 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argThat
import java.util.function.Predicate
@@ -68,7 +68,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var wallpaperManager: WallpaperManager
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val deviceStateManager = kosmos.deviceStateManager
@Mock
@@ -92,7 +92,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
surfaceControl1,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private var surfaceControl2 = mock(SurfaceControl::class.java)
@@ -113,7 +113,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
surfaceControl2,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
@@ -135,7 +135,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
surfaceControlWp,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
@@ -157,7 +157,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
surfaceControlLockWp,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
private var shouldPerformSmartspaceTransition = false
@@ -179,14 +179,14 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
notificationShadeWindowController,
powerManager,
wallpaperManager,
- deviceStateManager
+ deviceStateManager,
) {
override fun shouldPerformSmartspaceTransition(): Boolean =
shouldPerformSmartspaceTransition
}
keyguardUnlockAnimationController.setLauncherUnlockController(
"",
- launcherUnlockAnimationController
+ launcherUnlockAnimationController,
)
whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
@@ -227,7 +227,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
arrayOf(),
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
@@ -259,7 +259,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Since the animation is running, we should not have finished the remote animation.
@@ -282,7 +282,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
@@ -303,7 +303,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
@@ -327,7 +327,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
@@ -351,7 +351,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -373,7 +373,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -389,7 +389,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
@@ -406,7 +406,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -427,7 +427,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
lockWallpaperTargets,
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
for (i in 0..10) {
@@ -471,7 +471,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -492,7 +492,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- times(1).description("WallpaperSurface was expected to receive scheduleApply once")
+ times(1).description("WallpaperSurface was expected to receive scheduleApply once"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -523,7 +523,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -539,7 +539,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply")
+ atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -562,7 +562,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Stop the animator - we just want to test whether the override is not applied.
@@ -578,7 +578,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply")
+ atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -588,7 +588,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
assertEquals(
"Wallpaper surface was expected to have opacity 1",
1f,
- captorWp.getLastValue().alpha
+ captorWp.getLastValue().alpha,
)
verifyNoMoreInteractions(surfaceTransactionApplier)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 97c746c49cba..d0762a3797c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -25,10 +25,13 @@ import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.android.window.flags.Flags
@@ -63,6 +66,9 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
@Mock private lateinit var keyguardTransitions: KeyguardTransitions
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Before
fun setUp() {
@@ -77,6 +83,9 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
keyguardTransitions = keyguardTransitions,
+ selectedUserInteractor = selectedUserInteractor,
+ lockPatternUtils = lockPatternUtils,
+ keyguardShowWhileAwakeInteractor = keyguardShowWhileAwakeInteractor,
)
}
@@ -236,6 +245,8 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
.whenever(keyguardDismissTransitionInteractor)
.startDismissKeyguardTransition(any(), any())
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(-1)
+
underTest.onKeyguardGoingAwayRemoteAnimationStart(
transit = 0,
apps = arrayOf(mock<RemoteAnimationTarget>()),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 83bee7c66d31..fe213a6ebbf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -458,6 +458,56 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase()
@Test
@DisableSceneContainer
+ fun alpha_shadeExpansionIgnoredWhenTransitioningAwayFromLockscreen() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ shadeTestUtil.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(1f)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.STARTED,
+ value = 0f,
+ ),
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.RUNNING,
+ value = 0.8f,
+ ),
+ ),
+ testScope,
+ )
+ val priorAlpha = alpha
+ shadeTestUtil.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(priorAlpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ ),
+ testScope,
+ )
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @DisableSceneContainer
fun alphaFromShadeExpansion_doesNotEmitWhenTransitionRunning() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 91cb1ff266c9..9c168298b9a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -44,7 +45,7 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.collect.Range
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.runCurrent
@@ -101,20 +102,30 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
// immediately 0f
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+ assertThat(actual).isEqualTo(0f)
repository.sendTransitionStep(step(.2f))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+ assertThat(actual).isEqualTo(0f)
repository.sendTransitionStep(step(0.8f))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+ assertThat(actual).isEqualTo(0f)
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun lockscreenAlphaEndsWithZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.lockscreenAlpha)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+
+ // Jump right to the end and validate the value
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(alpha).isEqualTo(0f)
}
@Test
@@ -138,21 +149,17 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza
runCurrent()
// fade out
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(1f)
+ assertThat(actual).isEqualTo(1f)
repository.sendTransitionStep(step(.1f))
- runCurrent()
- Truth.assertThat(actual).isIn(Range.open(.1f, .9f))
+ assertThat(actual).isIn(Range.open(.1f, .9f))
// alpha is 1f before the full transition starts ending
repository.sendTransitionStep(step(0.8f))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+ assertThat(actual).isEqualTo(0f)
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
- runCurrent()
- Truth.assertThat(actual).isEqualTo(0f)
+ assertThat(actual).isEqualTo(0f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 61119cce7bc8..8592c424ca02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -235,6 +235,19 @@ class MediaCarouselScrollHandlerTest : SysuiTestCase() {
verify(mediaCarousel, never()).animationTargetX = anyFloat()
}
+ @Test
+ fun testScrollingDisabled_noScroll_notDismissible() {
+ setupMediaContainer(visibleIndex = 1, showsSettingsButton = false)
+
+ mediaCarouselScrollHandler.scrollingDisabled = true
+
+ clock.advanceTime(DISMISS_DELAY)
+ executor.runAllReady()
+
+ verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
+ verify(mediaCarousel, never()).animationTargetX = anyFloat()
+ }
+
private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) {
whenever(contentContainer.childCount).thenReturn(2)
val child1: View = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 6bc8000b0519..04f7fe1a6487 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -21,10 +21,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.statusbar.policy.fakeCastController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaRouterRepositoryTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
val testScope = kosmos.testScope
val castController = kosmos.fakeCastController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 858ed6a6b54a..473d7b6d0dfa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -22,8 +22,8 @@ import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@ class UserSettingObserverTest(flags: FlagsParameterization) : SysuiTestCase() {
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var testableLooper: TestableLooper
@@ -85,7 +85,7 @@ class UserSettingObserverTest(flags: FlagsParameterization) : SysuiTestCase() {
Handler(testableLooper.looper),
TEST_SETTING,
USER,
- DEFAULT_VALUE
+ DEFAULT_VALUE,
) {
override fun handleValueChanged(value: Int, observedChange: Boolean) {
callback(value, observedChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index 00490427f80b..a2829b5f42cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -27,7 +27,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
@@ -36,6 +35,7 @@ import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.WorkModeTile
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -47,7 +47,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val restoreProcessor: RestoreProcessor
get() = kosmos.workTileRestoreProcessor
@@ -62,7 +62,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
FakeUserTracker(
_userId = USER_INFO_0.id,
_userInfo = USER_INFO_0,
- _userProfiles = listOf(USER_INFO_0)
+ _userProfiles = listOf(USER_INFO_0),
)
underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index 3a9c3d066b23..4e0adcab8ea3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.FakeQSFactory
@@ -39,6 +38,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsTileFactory
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -55,22 +55,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NoLowNumberOfTilesTest : SysuiTestCase() {
- private val USER_0_INFO =
- UserInfo(
- 0,
- "zero",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ private val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
- private val defaultTiles =
- listOf(
- TileSpec.create("internet"),
- TileSpec.create("bt"),
- )
+ private val defaultTiles = listOf(TileSpec.create("internet"), TileSpec.create("bt"))
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
fakeUserTracker.set(listOf(USER_0_INFO), 0)
qsTileFactory = FakeQSFactory(::tileCreator)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index 9d9bfda99bd9..77030aceb477 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.FakeQSFactory
@@ -34,6 +33,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsTileFactory
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -52,7 +52,9 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
- private val kosmos by lazy { Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) } }
+ private val kosmos by lazy {
+ testKosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+ }
// Getter here so it can change when there is a managed profile.
private val workTileAvailable: Boolean
get() = hasManagedProfile()
@@ -143,30 +145,15 @@ class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
}
private fun TestScope.createManagedProfileAndAdd() {
- kosmos.fakeUserTracker.set(
- listOf(USER_0_INFO, MANAGED_USER_INFO),
- 0,
- )
+ kosmos.fakeUserTracker.set(listOf(USER_0_INFO, MANAGED_USER_INFO), 0)
runCurrent()
}
private companion object {
val WORK_TILE_SPEC = "work".toTileSpec()
- val USER_0_INFO =
- UserInfo(
- 0,
- "zero",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
val MANAGED_USER_INFO =
- UserInfo(
- 10,
- "ten-managed",
- "",
- 0,
- UserManager.USER_TYPE_PROFILE_MANAGED,
- )
+ UserInfo(10, "ten-managed", "", 0, UserManager.USER_TYPE_PROFILE_MANAGED)
fun String.toTileSpec() = TileSpec.create(this)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
index 557f4ea262a3..311f1f792bba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -23,13 +23,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class AirplaneModeMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
private lateinit var mapper: AirplaneModeMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 24e46686e23d..bb58ecaa2566 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -23,12 +23,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
import java.time.Instant
import java.time.LocalDateTime
@@ -41,7 +41,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AlarmTileMapperTest : SysuiTestCase() {
private val oneMinute = 60000L
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val alarmTileConfig = kosmos.qsAlarmTileConfig
private val fakeClock = FakeSystemClock()
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
index 44c175ab7156..1ef1a72b7483 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.utils.leaks.FakeBatteryController
import com.google.common.truth.Truth
import kotlinx.coroutines.flow.flowOf
@@ -40,7 +40,7 @@ import org.junit.runner.RunWith
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class BatterySaverTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val batteryController = FakeBatteryController(LeakCheck())
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 2ddaddd5ad35..cb50ec9a70f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -22,12 +22,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,7 +35,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class BatterySaverTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val batterySaverTileConfig = kosmos.qsBatterySaverTileConfig
private lateinit var mapper: BatterySaverTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index a3c159820a94..bcd443cc78e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -22,19 +22,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ColorCorrectionTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
private val subtitleArray by lazy {
context.resources.getStringArray(R.array.tile_states_color_correction)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index 83e61d1b36bf..3c0e7d51b4e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -34,6 +33,7 @@ import com.android.systemui.qs.tiles.impl.custom.customTileSpec
import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runCurrent
@@ -45,7 +45,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class CustomTileRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
+ private val kosmos = testKosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
private val underTest: CustomTileRepository =
with(kosmos) {
CustomTileRepositoryImpl(
@@ -213,7 +213,7 @@ class CustomTileRepositoryTest : SysuiTestCase() {
underTest.updateWithTile(
TEST_USER_1,
Tile().apply { subtitle = "test_subtitle" },
- true
+ true,
)
runCurrent()
@@ -247,7 +247,7 @@ class CustomTileRepositoryTest : SysuiTestCase() {
underTest.updateWithTile(
TEST_USER_1,
Tile().apply { subtitle = "test_subtitle" },
- true
+ true,
)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index a115c1235210..2da144e5ee98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -21,11 +21,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class FlashlightMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
private val mapper by lazy {
FlashlightMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index 8f8f7105415f..45720b86a859 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -22,19 +22,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class FontScalingTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val fontScalingTileConfig = kosmos.qsFontScalingTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 9bd4895ac21c..93f2bc34372e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -26,7 +26,6 @@ import com.android.systemui.accessibility.fontscaling.FontScalingDialogDelegate
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchableView
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.shared.QSSettingsPackageRepository
@@ -37,6 +36,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -67,7 +67,7 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() {
@Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val scope = kosmos.testScope
private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
private val keyguardStateController = FakeKeyguardStateController()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index 3d3447da15a1..c41034202c88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -21,19 +21,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
private val mapper by lazy {
HearingDevicesTileMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 4b9d11d2f706..4d38e7588578 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -24,11 +24,11 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -46,7 +46,7 @@ import org.mockito.kotlin.whenever
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
index 1b497a2b36ed..0ba057b1881b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -22,13 +22,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.shared.QSSettingsPackageRepository
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -47,7 +47,7 @@ import org.mockito.kotlin.whenever
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val inputHandler = FakeQSTileIntentUserInputHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 54a653df696f..e15664eba6b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -28,7 +28,6 @@ import com.android.systemui.common.shared.model.ContentDescription.Companion.loa
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
@@ -38,13 +37,14 @@ import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class InternetTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val internetTileConfig = kosmos.qsInternetTileConfig
private val handler = kosmos.fakeExecutorHandler
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 63607f1edd59..45582f017fb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -29,7 +29,6 @@ import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -54,6 +53,7 @@ import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkMode
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class InternetTileDataInteractorTest : SysuiTestCase() {
private val testUser = UserHandle.of(1)
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var underTest: InternetTileDataInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 261e3de939f2..d019a456a4f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@ import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -29,6 +28,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -44,7 +44,7 @@ import org.mockito.kotlin.verify
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class InternetTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private lateinit var underTest: InternetTileUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index 780d58c83e5b..48e114603723 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -22,20 +22,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ColorInversionTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
private val subtitleArrayId =
SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 7562ac00e4a6..57f85bd8c5ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -22,13 +22,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.flow.flowOf
@@ -42,7 +42,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class IssueRecordingDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val userTracker = kosmos.userTracker
private val userFileManager = kosmos.fakeUserFileManager
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
index fa6d8bf4a317..0201168181a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -20,7 +20,6 @@ import android.content.res.mainResources
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsEventLogger
@@ -30,6 +29,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.recordissue.RecordIssueModule
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class IssueRecordingMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val uiConfig =
QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label)
private val config =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 9c2be899b8ca..125419ba3bdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.dialogTransitionAnimator
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -39,6 +38,7 @@ import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.test.runTest
@@ -56,7 +56,7 @@ class IssueRecordingUserActionInteractorTest : SysuiTestCase() {
@Mock private lateinit var recordingController: RecordingController
val user = UserHandle(1)
- val kosmos = Kosmos().also { it.testCase = this }
+ val kosmos = testKosmos().also { it.testCase = this }
private lateinit var userContextProvider: UserContextProvider
private lateinit var underTest: IssueRecordingUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 4ebe23dcdef1..a6ad54935f08 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -21,11 +21,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import junit.framework.Assert
import org.junit.Test
@@ -34,7 +34,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LocationTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
index 8b21cb4a97d5..435aea07c9a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@ import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
@@ -32,6 +31,7 @@ import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTil
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@ class LocationTileUserActionInteractorTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
underTest =
LocationTileUserActionInteractor(
EmptyCoroutineContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
index a0aa2d4a9a6c..a5e2922ffb3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
@@ -26,7 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@ import org.mockito.ArgumentMatchers.anyInt
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val testStartTime = LocalTime.MIDNIGHT
private val testEndTime = LocalTime.NOON
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
index adc8bcba5a5c..51975b0508a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
@@ -26,12 +26,12 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.intentInputs
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -51,7 +51,7 @@ import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
private val testUser = UserHandle.of(1)
private val colorDisplayManager =
@@ -89,7 +89,7 @@ class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
NightDisplayTileUserActionInteractor(
nightDisplayRepository,
qsTileIntentUserActionHandler,
- kosmos.qsTileLogger
+ kosmos.qsTileLogger,
)
@Test
@@ -143,7 +143,7 @@ class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(
QSTileInputTestKtx.longClick(
NightDisplayTileModel.AutoModeOff(enabledState, false),
- testUser
+ testUser,
)
)
@@ -163,7 +163,7 @@ class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(
QSTileInputTestKtx.longClick(
NightDisplayTileModel.AutoModeOff(enabledState, false),
- testUser
+ testUser,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 7c853261aa1c..0b0b88e455da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -24,13 +24,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import java.time.LocalTime
import java.time.format.DateTimeFormatter
@@ -41,7 +41,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsNightDisplayTileConfig
private val testStartTime = LocalTime.MIDNIGHT
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
index b6caa22a3012..ecf6f2a9cd1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -22,19 +22,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import kotlin.test.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsNotesTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
index 4786fc40562a..84ab6905a007 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -24,9 +24,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toCollection
@@ -38,12 +38,11 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
private lateinit var underTest: NotesTileDataInteractor
-
@EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
@Test
fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
@@ -92,7 +91,7 @@ class NotesTileDataInteractorTest : SysuiTestCase() {
fun tileData_notEmpty() = runTest {
underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
val flowValue by
- collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+ collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
index 54911e612291..282af9159e2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -20,7 +20,6 @@ import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
@@ -29,18 +28,19 @@ import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val panelInteractor = mock<PanelInteractor>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
index 59eb069a5f3b..16b0caa78cfa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
@@ -22,9 +22,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.oneHandedModeRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
+import com.android.systemui.testKosmos
import com.android.wm.shell.onehanded.OneHanded
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
@@ -37,7 +37,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class OneHandedModeTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val oneHandedModeRepository = kosmos.oneHandedModeRepository
private val underTest: OneHandedModeTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 5b39810e3477..3fba857bae1a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -22,13 +22,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
import com.android.systemui.qs.tiles.impl.onehanded.qsOneHandedModeTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,7 +36,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class OneHandedModeTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsOneHandedModeTileConfig
private val subtitleArrayId = SubtitleArrayMapping.getSubtitleId(config.tileSpec.spec)
private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
index 312f18029570..ef21df6e2636 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -21,12 +21,12 @@ import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -36,7 +36,7 @@ import org.junit.runner.RunWith
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
private val inputHandler = kosmos.qsTileIntentUserInputHandler
private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
private val intent = mock<Intent>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index c572ff60b61a..9e20aae6103e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -23,11 +23,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
@@ -36,7 +36,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsQRCodeScannerTileConfig
private lateinit var mapper: QRCodeScannerTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
index dc3248d42d62..fecb3deaaf36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -23,10 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -40,14 +40,14 @@ import org.junit.runner.RunWith
class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {
private val isAvailable = true
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val reduceBrightColorsController = kosmos.reduceBrightColorsController
private val underTest: ReduceBrightColorsTileDataInteractor =
ReduceBrightColorsTileDataInteractor(
testScope.testScheduler,
isAvailable,
- reduceBrightColorsController
+ reduceBrightColorsController,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 75b090c4034b..77321385b09e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -28,11 +28,11 @@ import com.android.server.display.feature.flags.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.accessibility.reduceBrightColorsController
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -48,7 +48,7 @@ import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val controller = kosmos.reduceBrightColorsController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 00017f9059de..ebf70dad7149 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -23,12 +23,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,7 +36,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsReduceBrightColorsTileConfig
private lateinit var mapper: ReduceBrightColorsTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
index 283fa601f8df..6f05c3178754 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -28,9 +28,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository
import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.leaks.FakeBatteryController
@@ -48,7 +48,7 @@ import org.junit.runner.RunWith
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class RotationLockTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val batteryController = FakeBatteryController(LeakCheck())
private val rotationController = FakeRotationLockController(LeakCheck())
@@ -65,7 +65,7 @@ class RotationLockTileDataInteractorTest : SysuiTestCase() {
whenever(
packageManager.checkPermission(
eq(Manifest.permission.CAMERA),
- eq(TEST_PACKAGE_NAME)
+ eq(TEST_PACKAGE_NAME),
)
)
.thenReturn(PackageManager.PERMISSION_GRANTED)
@@ -81,7 +81,7 @@ class RotationLockTileDataInteractorTest : SysuiTestCase() {
.apply {
addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
}
- .resources
+ .resources,
)
}
@@ -182,7 +182,7 @@ class RotationLockTileDataInteractorTest : SysuiTestCase() {
whenever(
packageManager.checkPermission(
eq(Manifest.permission.CAMERA),
- eq(TEST_PACKAGE_NAME)
+ eq(TEST_PACKAGE_NAME),
)
)
.thenReturn(PackageManager.PERMISSION_DENIED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 74010143166b..0e6aaa6320c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -25,7 +25,6 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
@@ -33,6 +32,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -42,7 +42,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class RotationLockTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
private val devicePostureController = kosmos.devicePostureController
private val deviceStateManager = kosmos.deviceStateManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 1fb5048dd4c9..079921640705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -22,19 +22,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class DataSaverTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
index 41174e7ca6af..127160d8728d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -22,11 +22,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@ import org.junit.runner.RunWith
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val screenRecordRepo = kosmos.screenRecordRepository
private val underTest: ScreenRecordTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 778c73fd8638..f47e0578461a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@ import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -36,6 +35,7 @@ import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.testKosmos
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +49,7 @@ import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardInteractor = kosmos.keyguardInteractor
private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index 363255695131..d118c096fd16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -23,13 +23,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsScreenRecordTileConfig
private lateinit var mapper: ScreenRecordTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
index 6c7bb1b46c36..4a28fc02042d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
@@ -23,11 +23,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -44,7 +44,7 @@ import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val mockSensorPrivacyController =
mock<IndividualSensorPrivacyController> {
@@ -55,7 +55,7 @@ class SensorPrivacyToggleTileDataInteractorTest : SysuiTestCase() {
SensorPrivacyToggleTileDataInteractor(
testScope.testScheduler,
mockSensorPrivacyController,
- CAMERA
+ CAMERA,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
index 562e6ebcc029..7856fcaf4a1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -34,6 +33,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -48,7 +48,7 @@ import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val keyguardInteractor = kosmos.keyguardInteractor
// The keyguard repository below is the same one kosmos used to create the interactor above
@@ -64,7 +64,7 @@ class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
mockActivityStarter,
mockSensorPrivacyController,
fakeSafetyCenterManager,
- CAMERA
+ CAMERA,
)
@Test
@@ -79,7 +79,7 @@ class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
}
@@ -95,7 +95,7 @@ class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
}
@@ -114,7 +114,7 @@ class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
verify(mockActivityStarter).postQSRunnableDismissingKeyguard(any())
}
@@ -150,7 +150,7 @@ class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
mockActivityStarter,
mockSensorPrivacyController,
fakeSafetyCenterManager,
- MICROPHONE
+ MICROPHONE,
)
micUserActionInteractor.handleInput(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index e4cd0e0ec215..3b810dc451f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
@@ -33,13 +32,14 @@ import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileReso
import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.MicrophonePrivacyTileResources
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val cameraConfig = kosmos.qsCameraSensorPrivacyToggleTileConfig
private val micConfig = kosmos.qsMicrophoneSensorPrivacyToggleTileConfig
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 8f5f2d3e6689..547bbbc895a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -25,12 +25,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
import com.android.systemui.qs.tiles.impl.uimodenight.qsUiModeNightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import kotlin.reflect.KClass
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,7 +38,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class UiModeNightTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsUiModeNightTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 2c81f39a03ec..ed3fc5058c44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -26,12 +26,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.qs.tiles.impl.work.qsWorkModeTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -43,7 +43,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class WorkModeTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsWorkModeTileConfig
private val devicePolicyManager = kosmos.devicePolicyManager
private val testLabel = context.getString(R.string.quick_settings_work_mode_label)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index a8b005fb6554..12ed3f0c96fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -30,7 +30,6 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorI
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -40,6 +39,7 @@ import com.android.systemui.qs.dagger.QSSceneComponent
import com.android.systemui.settings.brightness.MirrorController
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -65,7 +65,7 @@ import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
class QSSceneAdapterImplTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { testCase = this@QSSceneAdapterImplTest }
+ private val kosmos = testKosmos().apply { testCase = this@QSSceneAdapterImplTest }
private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 7bcaeabfee69..390a5d8efff1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -28,12 +28,12 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.traceur.TraceConfig
import com.google.common.truth.Truth
@@ -52,7 +52,7 @@ import org.mockito.kotlin.verify
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class IssueRecordingServiceSessionTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val bgExecutor = kosmos.fakeExecutor
private val userContextProvider: UserContextProvider = kosmos.userTracker
private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 83bdcd2161ee..0510e6ca3ced 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -22,9 +22,9 @@ import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import org.junit.Before
@@ -40,7 +40,7 @@ import org.mockito.kotlin.verify
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class IssueRecordingStateTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private lateinit var underTest: IssueRecordingState
@Mock private lateinit var resolver: ContentResolver
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
index 737b10166199..6f0dd16eacd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
@@ -20,10 +20,10 @@ import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import org.junit.Before
import org.junit.Test
@@ -34,7 +34,7 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class ScreenRecordingStartTimeStoreTest : SysuiTestCase() {
- private val userTracker: UserTracker = Kosmos().also { it.testCase = this }.userTracker
+ private val userTracker: UserTracker = testKosmos().also { it.testCase = this }.userTracker
private lateinit var underTest: ScreenRecordingStartTimeStore
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index a82a7de75cc0..7e9487b04862 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -25,7 +25,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -38,6 +37,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.init.NotificationsController
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -57,7 +57,7 @@ import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testDispatcher = StandardTestDispatcher()
private val iStatusBarService = mock<IStatusBarService>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index 9724974e3044..bd5416676c8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -21,10 +21,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -39,7 +39,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val recordingController = mock<RecordingController>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 268d62952fc7..3788049256a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -41,6 +41,8 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -76,6 +78,7 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -94,6 +97,9 @@ import java.util.concurrent.Executor;
@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
+ @Rule public final CheckFlagsRule checkFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock private ViewCaptureAwareWindowManager mWindowManager;
@Mock private DozeParameters mDozeParameters;
@Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
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 c6ce58185cf0..0c90d077273d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -25,7 +25,6 @@ import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
import com.android.systemui.assist.AssistManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -43,6 +42,7 @@ 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.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -63,7 +63,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
private val executor = FakeExecutor(FakeSystemClock())
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@Mock private lateinit var commandQueue: CommandQueue
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 054c1b8207eb..32eec56af8ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -27,7 +27,6 @@ import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -35,6 +34,7 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,7 +52,7 @@ import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
@EnableSceneContainer
class ShadeControllerSceneImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ddad230f04e9..2f2fafab53d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -25,8 +25,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
@@ -543,7 +543,7 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun jsonDeserialization_fontAxes() {
- val expected = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+ val expected = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
val json = JSONObject("""{"axes":[{"key":"KEY","value":10}]}""")
val actual = ClockSettings.fromJson(json)
assertEquals(expected, actual)
@@ -576,7 +576,7 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun jsonSerialization_axisSettings() {
- val settings = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+ val settings = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
val actual = ClockSettings.toJson(settings)
val expected = JSONObject("""{"metadata":{},"axes":[{"key":"KEY","value":10}]}""")
assertEquals(expected.toString(), actual.toString())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 01046cd10d87..3c19179bc1c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -227,7 +227,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
if (NotificationBundleUi.isEnabled()) {
return row.getEntryAdapter().getSbn().getNotification();
} else {
- return row.getEntry().getSbn().getNotification();
+ return row.getEntryLegacy().getSbn().getNotification();
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 326d8ffd3c7c..3ecf302204bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -32,14 +32,12 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.eq
@@ -77,7 +75,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val applicationScope = kosmos.testScope.backgroundScope
- @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -87,6 +84,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var wallpaperController: WallpaperController
@Mock private lateinit var wallpaperInteractor: WallpaperInteractor
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ @Mock private lateinit var shadeModeInteractor: ShadeModeInteractor
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
@Mock private lateinit var root: View
@@ -103,7 +102,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
private var statusBarState = StatusBarState.SHADE
private val maxBlur = 150
private lateinit var notificationShadeDepthController: NotificationShadeDepthController
- private val configurationController = FakeConfigurationController()
@Before
fun setup() {
@@ -133,13 +131,11 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
wallpaperInteractor,
notificationShadeWindowController,
dozeParameters,
- context,
- ResourcesSplitShadeStateController(),
+ shadeModeInteractor,
windowRootViewBlurInteractor,
appZoomOutOptional,
applicationScope,
- dumpManager,
- configurationController,
+ dumpManager
)
notificationShadeDepthController.shadeAnimation = shadeAnimation
notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -492,15 +488,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
private fun enableSplitShade() {
- setSplitShadeEnabled(true)
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
}
private fun disableSplitShade() {
- setSplitShadeEnabled(false)
- }
-
- private fun setSplitShadeEnabled(enabled: Boolean) {
- overrideResource(R.bool.config_use_split_notification_shade, enabled)
- configurationController.notifyConfigurationChanged()
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 03dee3aeb75c..72d21f1064af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -24,13 +24,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.kotlin.JavaAdapter
@@ -54,7 +54,7 @@ class OperatorNameViewControllerTest : SysuiTestCase() {
private lateinit var underTest: OperatorNameViewController
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = TestScope()
private val view = OperatorNameView(mContext)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 485b9febc284..2fa9a02b9e87 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -17,12 +17,17 @@
package com.android.systemui.statusbar.chips.call.ui.viewmodel
import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -51,6 +56,7 @@ import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -481,6 +487,294 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
}
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_updatesCorrectly_withStateAndTransitionState() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start off with no call.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+ // Call starts [NoCall -> InCall(isAppVisible=true), NoTransition].
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+ val factory = latest!!.transitionManager!!.controllerFactory
+ assertThat(factory!!.component).isEqualTo(component)
+
+ // Request a return transition [InCall(isAppVisible=true), NoTransition ->
+ // ReturnRequested].
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Start the return transition [InCall(isAppVisible=true), ReturnRequested ->
+ // Returning].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the return transition [InCall(isAppVisible=true), Returning -> NoTransition].
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Settle the return transition [InCall(isAppVisible=true) ->
+ // InCall(isAppVisible=false), NoTransition].
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Trigger a launch transition [InCall(isAppVisible=false) -> InCall(isAppVisible=true),
+ // NoTransition].
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, true)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Request the return transition [InCall(isAppVisible=true), NoTransition ->
+ // LaunchRequested].
+ controller = factory.createController(forLaunch = true)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Start the return transition [InCall(isAppVisible=true), LaunchRequested ->
+ // Launching].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the return transition [InCall(isAppVisible=true), Launching -> NoTransition].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the call with the app visible [InCall(isAppVisible=true) -> NoCall,
+ // NoTransition].
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+ // End the call with the app hidden [InCall(isAppVisible=false) -> NoCall,
+ // NoTransition].
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ isAppVisible = false,
+ )
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+ }
+
+ @Test
+ @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ fun chipLegacy_hasNoTransitionAnimationInformation() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ // NoCall
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest!!.transitionManager).isNull()
+
+ // InCall with visible app
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest!!.transitionManager).isNull()
+
+ // InCall with hidden app
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+ assertThat(latest!!.transitionManager).isNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_chipDataChangesMidTransition() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start with the app visible and trigger a return animation.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ var factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip changes state.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 0,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // Reset the state and trigger a launch animation.
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ controller = factory.createController(forLaunch = true)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip changes state.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = -2,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_chipDisappearsMidTransition() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start with the app visible and trigger a return animation.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ var factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip disappears.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+
+ // Reset the state and trigger a launch animation.
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ controller = factory.createController(forLaunch = true)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip disappears.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ }
+
companion object {
fun createStatusBarIconViewOrNull(): StatusBarIconView? =
if (StatusBarConnectedDisplays.isEnabled) {
@@ -500,6 +794,8 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
.build()
+ private const val NOTIFICATION_KEY = "testKey"
+ private const val NOTIFICATION_UID = 12345
private const val PROMOTED_BACKGROUND_COLOR = 65
private const val PROMOTED_PRIMARY_TEXT_COLOR = 98
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
index b2174c1b1d8c..21a4560dafee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
@@ -20,12 +20,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.mediaRouterChipInteractor
import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -35,7 +35,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaRouterChipInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
val testScope = kosmos.testScope
val mediaRouterRepository = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
index 274efbb50788..568129706458 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
@@ -30,7 +30,6 @@ import android.view.Window
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.me
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndCastScreenToOtherDeviceDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndCastScreenToOtherDeviceDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
index 88207d1c61d8..30a415cb9906 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
@@ -26,7 +26,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
@@ -35,6 +34,7 @@ import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -51,7 +51,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndGenericCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndGenericCastToOtherDeviceDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index ccc844ad5837..d921ab3b284d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -30,7 +30,6 @@ import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -52,6 +51,7 @@ import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -69,7 +69,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
private val mediaRouterRepo = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
index 795988f0087f..fac50b38d838 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -24,12 +24,12 @@ import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith
@@ -42,7 +42,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val underTest = kosmos.endMediaProjectionDialogHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 4993b5661373..b5cfc7e9080d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -573,6 +573,8 @@ class NotifChipsViewModelTest : SysuiTestCase() {
assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(whenElapsed)
+ assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).isEventInFuture)
+ .isFalse()
}
@Test
@@ -608,6 +610,8 @@ class NotifChipsViewModelTest : SysuiTestCase() {
assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(whenElapsed)
+ assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).isEventInFuture)
+ .isTrue()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
index f560ee7730ae..981c9525f636 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -30,7 +30,6 @@ import android.view.Window
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -39,6 +38,7 @@ import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -55,7 +55,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
index 95aa6cd3250b..b2e90ecbeef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
@@ -30,7 +30,6 @@ import android.view.Window
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.me
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndShareScreenToAppDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndShareScreenToAppDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index e3a84fd2c2eb..6d91fb5a10cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -22,12 +22,12 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,7 +39,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ChipTransitionHelperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
index 4e92540396d3..cd9970cfa614 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
@@ -35,55 +35,153 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ChronometerStateTest : SysuiTestCase() {
- private lateinit var mockTimeSource: MutableTimeSource
+ private lateinit var fakeTimeSource: MutableTimeSource
@Before
fun setup() {
- mockTimeSource = MutableTimeSource()
+ fakeTimeSource = MutableTimeSource()
}
@Test
- fun initialText_isCorrect() = runTest {
- val state = ChronometerState(mockTimeSource, 0L)
- assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(0))
+ fun initialText_isEventInFutureFalse_timeIsNow() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 3_000, isEventInFuture = false)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 0))
}
@Test
- fun textUpdates_withTime() = runTest {
- val startTime = 1000L
- val state = ChronometerState(mockTimeSource, startTime)
+ fun initialText_isEventInFutureFalse_timeInPast() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 1_000, isEventInFuture = false)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 2))
+ }
+
+ @Test
+ fun initialText_isEventInFutureFalse_timeInFuture() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 5_000, isEventInFuture = false)
+ // When isEventInFuture=false, eventTimeMillis needs to be in the past if we want text to
+ // show
+ assertThat(state.currentTimeText).isNull()
+ }
+
+ @Test
+ fun initialText_isEventInFutureTrue_timeIsNow() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 3_000, isEventInFuture = true)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 0))
+ }
+
+ @Test
+ fun initialText_isEventInFutureTrue_timeInFuture() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 5_000, isEventInFuture = true)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 2))
+ }
+
+ @Test
+ fun initialText_isEventInFutureTrue_timeInPast() = runTest {
+ fakeTimeSource.time = 3_000
+ val state =
+ ChronometerState(fakeTimeSource, eventTimeMillis = 1_000, isEventInFuture = true)
+ // When isEventInFuture=true, eventTimeMillis needs to be in the future if we want text to
+ // show
+ assertThat(state.currentTimeText).isNull()
+ }
+
+ @Test
+ fun textUpdates_isEventInFutureFalse_timeInPast() = runTest {
+ val eventTime = 1000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
val job = launch { state.run() }
val elapsedTime = 5000L
- mockTimeSource.time = startTime + elapsedTime
+ fakeTimeSource.time = eventTime + elapsedTime
advanceTimeBy(elapsedTime)
assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
+ val additionalTime = 6000L
+ fakeTimeSource.time += additionalTime
+ advanceTimeBy(additionalTime)
+ assertThat(state.currentTimeText)
+ .isEqualTo(formatElapsedTime((elapsedTime + additionalTime) / 1000))
+
job.cancelAndJoin()
}
@Test
- fun textUpdates_toLargerValue() = runTest {
- val startTime = 1000L
- val state = ChronometerState(mockTimeSource, startTime)
+ fun textUpdates_isEventInFutureFalse_timeChangesFromFutureToPast() = runTest {
+ val eventTime = 15_000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
val job = launch { state.run() }
- val elapsedTime = 15000L
- mockTimeSource.time = startTime + elapsedTime
- advanceTimeBy(elapsedTime)
- assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
+ // WHEN the time is 5 but the eventTime is 15
+ fakeTimeSource.time = 5_000L
+ advanceTimeBy(5_000L)
+ // THEN no text is shown
+ assertThat(state.currentTimeText).isNull()
+
+ // WHEN the time advances to 40
+ fakeTimeSource.time = 40_000L
+ advanceTimeBy(35_000)
+ // THEN text is shown as 25 seconds (40 - 15)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 25))
job.cancelAndJoin()
}
@Test
- fun textUpdates_afterResettingBase() = runTest {
+ fun textUpdates_isEventInFutureTrue_timeInFuture() = runTest {
+ val eventTime = 15_000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+ val job = launch { state.run() }
+
+ fakeTimeSource.time = 5_000L
+ advanceTimeBy(5_000L)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+ val additionalTime = 6000L
+ fakeTimeSource.time += additionalTime
+ advanceTimeBy(additionalTime)
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 4))
+
+ job.cancelAndJoin()
+ }
+
+ @Test
+ fun textUpdates_isEventInFutureTrue_timeChangesFromFutureToPast() = runTest {
+ val eventTime = 15_000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+ val job = launch { state.run() }
+
+ // WHEN the time is 5 and the eventTime is 15
+ fakeTimeSource.time = 5_000L
+ advanceTimeBy(5_000L)
+ // THEN 10 seconds is shown
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+ // WHEN the time advances to 40 (past the event time)
+ fakeTimeSource.time = 40_000L
+ advanceTimeBy(35_000)
+ // THEN no text is shown
+ assertThat(state.currentTimeText).isNull()
+
+ job.cancelAndJoin()
+ }
+
+ @Test
+ fun textUpdates_afterResettingBase_isEventInFutureFalse() = runTest {
val initialElapsedTime = 30000L
val startTime = 50000L
- val state = ChronometerState(mockTimeSource, startTime)
+ val state = ChronometerState(fakeTimeSource, startTime, isEventInFuture = false)
val job = launch { state.run() }
- mockTimeSource.time = startTime + initialElapsedTime
+ fakeTimeSource.time = startTime + initialElapsedTime
advanceTimeBy(initialElapsedTime)
assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(initialElapsedTime / 1000))
@@ -91,15 +189,68 @@ class ChronometerStateTest : SysuiTestCase() {
val newElapsedTime = 5000L
val newStartTime = 100000L
- val newState = ChronometerState(mockTimeSource, newStartTime)
+ val newState = ChronometerState(fakeTimeSource, newStartTime, isEventInFuture = false)
val newJob = launch { newState.run() }
- mockTimeSource.time = newStartTime + newElapsedTime
+ fakeTimeSource.time = newStartTime + newElapsedTime
advanceTimeBy(newElapsedTime)
assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(newElapsedTime / 1000))
newJob.cancelAndJoin()
}
+
+ @Test
+ fun textUpdates_afterResettingBase_isEventInFutureTrue() = runTest {
+ val initialElapsedTime = 40_000L
+ val eventTime = 50_000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+ val job = launch { state.run() }
+
+ fakeTimeSource.time = initialElapsedTime
+ advanceTimeBy(initialElapsedTime)
+ // Time should be 50 - 40 = 10
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+ job.cancelAndJoin()
+
+ val newElapsedTime = 75_000L
+ val newEventTime = 100_000L
+ val newState = ChronometerState(fakeTimeSource, newEventTime, isEventInFuture = true)
+ val newJob = launch { newState.run() }
+
+ fakeTimeSource.time = newElapsedTime
+ advanceTimeBy(newElapsedTime - initialElapsedTime)
+ // Time should be 100 - 75 = 25
+ assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 25))
+
+ newJob.cancelAndJoin()
+ }
+
+ @Test
+ fun textUpdates_afterResettingisEventInFuture() = runTest {
+ val initialElapsedTime = 40_000L
+ val eventTime = 50_000L
+ val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+ val job = launch { state.run() }
+
+ fakeTimeSource.time = initialElapsedTime
+ advanceTimeBy(initialElapsedTime)
+ // Time should be 50 - 40 = 10
+ assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+ job.cancelAndJoin()
+
+ val newElapsedTime = 70_000L
+ val newState = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
+ val newJob = launch { newState.run() }
+
+ fakeTimeSource.time = newElapsedTime
+ advanceTimeBy(newElapsedTime - initialElapsedTime)
+ // Time should be 70 - 50 = 20
+ assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 20))
+
+ newJob.cancelAndJoin()
+ }
}
/** A fake implementation of [TimeSource] that allows the caller to set the current time */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index f06244f4f637..7135cf01394a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -931,7 +931,40 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun visibleChipKeys_fourPromotedNotifs_topThreeInList() =
+ @DisableChipsModernization
+ fun visibleChipKeys_chipsModOff_threePromotedNotifs_topTwoInList() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipKeys)
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "firstNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("firstNotif").build(),
+ ),
+ activeNotificationModel(
+ key = "secondNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("secondNotif").build(),
+ ),
+ activeNotificationModel(
+ key = "thirdNotif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent =
+ PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ ),
+ )
+ )
+
+ assertThat(latest).containsExactly("firstNotif", "secondNotif").inOrder()
+ }
+
+ @Test
+ @EnableChipsModernization
+ fun visibleChipKeys_chipsModOn_fourPromotedNotifs_topThreeInList() =
kosmos.runTest {
val latest by collectLastValue(underTest.visibleChipKeys)
@@ -1069,7 +1102,37 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun visibleChipKeys_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
+ @DisableChipsModernization
+ fun visibleChipKeys_chipsModOff_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.visibleChipKeys)
+
+ val callNotificationKey = "call"
+ addOngoingCallState(callNotificationKey)
+ screenRecordState.value = ScreenRecordModel.Recording
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ )
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ )
+ )
+
+ assertThat(latest)
+ .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey)
+ .inOrder()
+ }
+
+ @Test
+ @EnableChipsModernization
+ fun visibleChipKeys_chipsModOn_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
kosmos.runTest {
val latest by collectLastValue(underTest.visibleChipKeys)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 2f6bedb42e45..ea61b715475d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -29,7 +29,6 @@ import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.layout.BoundsPair
@@ -42,6 +41,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
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.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -59,7 +59,7 @@ import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarModeRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = TestScope()
private val commandQueue = mock<CommandQueue>()
private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 790b2c343a11..bfd700dcc302 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.util.time.FakeSystemClock;
@@ -151,7 +152,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isBlockable());
}
@@ -251,7 +253,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -365,7 +368,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
@@ -378,7 +382,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
index ef0a4169d98e..d532010f4c55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -323,7 +324,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN group changes aren't allowed
assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
@@ -349,7 +353,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN the notification list is invalidated
verifyStabilityManagerWasInvalidated(times(1))
@@ -365,7 +372,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from reordering
verifyStabilityManagerWasInvalidated(never())
@@ -382,7 +392,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from
// reordering;
@@ -415,7 +428,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// can now reorder, so invalidates
verifyStabilityManagerWasInvalidated(times(1))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 3116143504eb..893c17998a17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -376,12 +376,67 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBigTextStyle() {
- val entry = createEntry { setStyle(BigTextStyle()) }
+ val entry = createEntry {
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ setStyle(
+ BigTextStyle()
+ .bigText(TEST_BIG_TEXT)
+ .setBigContentTitle(TEST_BIG_CONTENT_TITLE)
+ .setSummaryText(TEST_SUMMARY_TEXT)
+ )
+ }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.BigText)
+ assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromBigTextStyle_fallbackToContentTitle() {
+ val entry = createEntry {
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ setStyle(
+ BigTextStyle()
+ .bigText(TEST_BIG_TEXT)
+ // bigContentTitle unset
+ .setSummaryText(TEST_SUMMARY_TEXT)
+ )
+ }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.BigText)
+ assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromBigTextStyle_fallbackToContentText() {
+ val entry = createEntry {
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ setStyle(
+ BigTextStyle()
+ // bigText unset
+ .setBigContentTitle(TEST_BIG_CONTENT_TITLE)
+ .setSummaryText(TEST_SUMMARY_TEXT)
+ )
+ }
val content = extractContent(entry)
assertThat(content).isNotNull()
assertThat(content?.style).isEqualTo(Style.BigText)
+ assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
}
@Test
@@ -498,6 +553,10 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
private const val TEST_CONTENT_TEXT = "content text"
private const val TEST_SHORT_CRITICAL_TEXT = "short"
+ private const val TEST_BIG_CONTENT_TITLE = "big content title"
+ private const val TEST_BIG_TEXT = "big text"
+ private const val TEST_SUMMARY_TEXT = "summary text"
+
private const val TEST_PROGRESS = 50
private const val TEST_PROGRESS_MAX = 100
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 1b8d64d5483c..387c62d76083 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -80,6 +81,9 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -107,9 +111,6 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
/** Tests for [NotificationGutsManager]. */
@SmallTest
@@ -149,6 +150,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
@Mock private lateinit var launcherApps: LauncherApps
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -214,6 +216,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -509,6 +512,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -521,6 +525,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -530,6 +535,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(true),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -545,6 +551,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -560,6 +567,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -569,6 +577,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(true),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -584,6 +593,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -597,6 +607,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -606,6 +617,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 96ae07035ed2..0ac5fe95957c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -49,6 +49,7 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ImageView
import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
@@ -57,17 +58,18 @@ import com.android.internal.logging.metricsLogger
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.testKosmos
import com.android.telecom.telecomManager
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
@@ -89,7 +91,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationInfoTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private lateinit var underTest: NotificationInfo
private lateinit var notificationChannel: NotificationChannel
@@ -105,6 +107,7 @@ class NotificationInfoTest : SysuiTestCase() {
private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
private val mockINotificationManager = mock<INotificationManager>()
private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+ private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
private val assistantFeedbackController = mock<AssistantFeedbackController>()
@Before
@@ -863,6 +866,31 @@ class NotificationInfoTest : SysuiTestCase() {
assertThat(underTest.findViewById<View>(R.id.feedback).visibility).isEqualTo(GONE)
}
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissListenerBound() {
+ val latch = CountDownLatch(1)
+ bindNotification(onCloseClick = { _: View? -> latch.countDown() })
+
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isTrue()
+ dismissView.performClick()
+
+ // Verify that listener was triggered.
+ assertThat(latch.count).isEqualTo(0)
+ }
+
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissHiddenWhenUndismissable() {
+
+ entry.sbn.notification.flags =
+ entry.sbn.notification.flags or android.app.Notification.FLAG_NO_DISMISS
+ bindNotification(isDismissable = false)
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isFalse()
+ }
+
private fun bindNotification(
pm: PackageManager = this.mockPackageManager,
iNotificationManager: INotificationManager = this.mockINotificationManager,
@@ -871,6 +899,7 @@ class NotificationInfoTest : SysuiTestCase() {
onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
channelEditorDialogController: ChannelEditorDialogController =
this.channelEditorDialogController,
+ packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
pkg: String = TEST_PACKAGE_NAME,
notificationChannel: NotificationChannel = this.notificationChannel,
entry: NotificationEntry = this.entry,
@@ -880,6 +909,7 @@ class NotificationInfoTest : SysuiTestCase() {
uiEventLogger: UiEventLogger = this.uiEventLogger,
isDeviceProvisioned: Boolean = true,
isNonblockable: Boolean = false,
+ isDismissable: Boolean = true,
wasShownHighPriority: Boolean = true,
assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
metricsLogger: MetricsLogger = kosmos.metricsLogger,
@@ -892,6 +922,7 @@ class NotificationInfoTest : SysuiTestCase() {
iconStyleProvider,
onUserInteractionCallback,
channelEditorDialogController,
+ packageDemotionInteractor,
pkg,
notificationChannel,
entry,
@@ -901,6 +932,7 @@ class NotificationInfoTest : SysuiTestCase() {
uiEventLogger,
isDeviceProvisioned,
isNonblockable,
+ isDismissable,
wasShownHighPriority,
assistantFeedbackController,
metricsLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index af67a04d2f2a..2d4063b2f667 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableResources;
import android.view.View;
@@ -39,7 +38,6 @@ import androidx.test.annotation.UiThreadTest;
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.animation.AnimatorTestRule;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -93,7 +91,6 @@ public class NotificationSnoozeTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
public void closeControls_withoutSave_performsUndo() {
ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
mUnderTest.mSelectedOption = options.getFirst();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 5638e0b434aa..209dfb2d2ed6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -48,6 +48,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -92,6 +93,8 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
@Mock
private ChannelEditorDialogController mChannelEditorDialogController;
@Mock
+ private PackageDemotionInteractor mPackageDemotionInteractor;
+ @Mock
private AssistantFeedbackController mAssistantFeedbackController;
@Mock
private TelecomManager mTelecomManager;
@@ -138,6 +141,7 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
mMockIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
TEST_PACKAGE_NAME,
mNotificationChannel,
mEntry,
@@ -148,6 +152,7 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
true,
false,
true,
+ true,
mAssistantFeedbackController,
mMetricsLogger,
null);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 048028cdc0fa..db0c59f6d602 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -23,12 +23,12 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.statusbar.lockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -44,7 +44,7 @@ import org.mockito.kotlin.whenever
class NotificationShelfInteractorTest : SysuiTestCase() {
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
testCase = this@NotificationShelfInteractorTest
lockscreenShadeTransitionController = mock()
screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 6381b4e0fef7..7265262183ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -24,7 +24,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testCase
@@ -35,6 +34,7 @@ import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.statusbar.lockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -50,7 +50,7 @@ import org.mockito.kotlin.whenever
class NotificationShelfViewModelTest : SysuiTestCase() {
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
testCase = this@NotificationShelfViewModelTest
lockscreenShadeTransitionController = mock()
screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index c5abd026b369..19e98387a120 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -106,6 +106,7 @@ public final class NotificationGroupTestHelper {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
return entry;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index eb95ddb39b40..c58b4bc9953c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -30,7 +30,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
@@ -48,6 +47,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingC
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.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -70,7 +70,7 @@ import org.mockito.kotlin.whenever
@TestableLooper.RunWithLooper
@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallControllerTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val mainExecutor = kosmos.fakeExecutor
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index a44631348796..7de56ddc06c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -19,12 +19,11 @@ package com.android.systemui.statusbar.phone.ongoingcall.data.repository
import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,7 +32,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.ongoingCallRepository
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index f4204af7829b..84f1d5cd4895 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -23,7 +23,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.activity.data.repository.fake
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -36,6 +35,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCall
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -51,7 +51,7 @@ import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@EnableChipsModernization
class OngoingCallInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val underTest = kosmos.ongoingCallInteractor
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 91b3896332f5..39cf02dbc772 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -54,9 +54,7 @@ class FakeHomeStatusBarViewModel(
MutableStateFlow(OngoingActivityChipModel.Inactive())
override val ongoingActivityChips =
- MutableStateFlow(
- ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
- )
+ ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
override val ongoingActivityChipsLegacy =
MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 2da692b4cb45..20cf3ae6b8dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -49,6 +49,7 @@ import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.log.assertLogsWtf
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -107,7 +108,8 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class HomeStatusBarViewModelImplTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
+ private val Kosmos.underTest by
+ Kosmos.Fixture { kosmos.homeStatusBarViewModel.also { it.activateIn(kosmos.testScope) } }
@Before
fun setUp() {
@@ -891,32 +893,26 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
// home status bar not allowed
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
@@ -924,7 +920,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -932,8 +928,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -943,7 +937,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -951,8 +945,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -962,16 +954,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
@EnableChipsModernization
- fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
+ fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -981,7 +971,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -989,8 +979,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -1000,7 +988,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -1008,17 +996,16 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_followsChipsViewModel() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
transitionKeyguardToGone()
screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
- assertIsScreenRecordChip(latest!!.chips.active[0])
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
addOngoingCallState(key = "call")
- assertIsScreenRecordChip(latest!!.chips.active[0])
- assertIsCallChip(latest!!.chips.active[1], "call", context)
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
+ assertIsCallChip(underTest.ongoingActivityChips.chips.active[1], "call", context)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 9a0b8125fb25..b8be34378891 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -282,6 +282,33 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.TONAL_SPOT);
}
+ @Test
+ @HardwareColors(color = "BLK", options = {
+ "BLK|MONOCHROMATIC|#FF0000",
+ "*|VIBRANT|home_wallpaper"
+ })
+ @EnableFlags(com.android.systemui.Flags.FLAG_HARDWARE_COLOR_STYLES)
+ public void start_checkHardwareColor_storeInSecureSetting() {
+ // getWallpaperColors should not be called
+ ArgumentCaptor<Runnable> registrationRunnable = ArgumentCaptor.forClass(Runnable.class);
+ verify(mMainExecutor).execute(registrationRunnable.capture());
+ registrationRunnable.getValue().run();
+ verify(mWallpaperManager, never()).getWallpaperColors(anyInt());
+
+ assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.MONOCHROMATIC);
+ assertThat(mThemeOverlayController.mCurrentColors.get(0).getMainColors().get(
+ 0).toArgb()).isEqualTo(Color.RED);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings).putStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
+ anyInt());
+
+ assertThat(updatedSetting.getValue().contains(
+ "android.theme.customization.theme_style\":\"MONOCHROMATIC")).isTrue();
+ assertThat(updatedSetting.getValue().contains(
+ "android.theme.customization.system_palette\":\"ffff0000")).isTrue();
+ }
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 92bec70d2c4d..18a124cf362e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -38,7 +38,6 @@ import com.android.systemui.display.data.repository.DeviceStateRepository.Device
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
@@ -47,6 +46,7 @@ import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_OFF
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.COOL_DOWN_DURATION
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
@@ -89,7 +89,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
@Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
private val foldStateRepository = kosmos.fakeDeviceStateRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index dfc4d0f07df8..98d99f781879 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -28,7 +28,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -36,6 +35,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.testKosmos
import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
@@ -61,7 +61,7 @@ import org.mockito.kotlin.whenever
class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope: TestScope = kosmos.testScope
private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
private val powerInteractor = kosmos.powerInteractor
@@ -93,7 +93,7 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
fakeAnimationStatusRepository,
mockControllerFactory,
mockFoldLockSettingAvailabilityProvider,
- mockJankMonitor
+ mockJankMonitor,
)
foldLightRevealAnimation.init()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
index b4ba41a5c524..a180d51ff0ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
@@ -27,7 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceStateManager
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
@@ -39,7 +39,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class UtilsTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val deviceStateManager = kosmos.deviceStateManager
private lateinit var testableResources: TestableResources
@@ -53,7 +53,7 @@ class UtilsTest : SysuiTestCase() {
fun isFoldableReturnsFalse_overlayConfigurationValues() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf() // empty array <=> device is not foldable
+ intArrayOf(), // empty array <=> device is not foldable
)
whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -64,7 +64,7 @@ class UtilsTest : SysuiTestCase() {
fun isFoldableReturnsFalse_deviceStateManager() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf() // empty array <=> device is not foldable
+ intArrayOf(), // empty array <=> device is not foldable
)
whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -75,7 +75,7 @@ class UtilsTest : SysuiTestCase() {
fun isFoldableReturnsTrue_overlayConfigurationValues() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf(FOLDED_DEVICE_STATE.identifier)
+ intArrayOf(FOLDED_DEVICE_STATE.identifier),
)
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
@@ -87,7 +87,7 @@ class UtilsTest : SysuiTestCase() {
fun isFoldableReturnsTrue_deviceStateManager() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf(FOLDED_DEVICE_STATE.identifier)
+ intArrayOf(FOLDED_DEVICE_STATE.identifier),
)
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index f232d52615a4..93e5721e9b27 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -20,8 +20,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.volume.panel.domain.availableCriteria
import com.android.systemui.volume.panel.domain.defaultCriteria
import com.android.systemui.volume.panel.domain.model.ComponentModel
@@ -39,7 +39,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ComponentsInteractorImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private lateinit var underTest: ComponentsInteractor
@@ -60,12 +60,7 @@ class ComponentsInteractorImplTest : SysuiTestCase() {
fun componentsAvailability_checked() {
with(kosmos) {
testScope.runTest {
- enabledComponents =
- setOf(
- BOTTOM_BAR,
- COMPONENT_1,
- COMPONENT_2,
- )
+ enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
criteriaByKey =
mapOf(
BOTTOM_BAR to Provider { availableCriteria },
@@ -90,12 +85,7 @@ class ComponentsInteractorImplTest : SysuiTestCase() {
fun noCriteria_fallbackToDefaultCriteria() {
with(kosmos) {
testScope.runTest {
- enabledComponents =
- setOf(
- BOTTOM_BAR,
- COMPONENT_1,
- COMPONENT_2,
- )
+ enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
criteriaByKey =
mapOf(
BOTTOM_BAR to Provider { availableCriteria },
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
index 235475f6b202..0fc3470716fe 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
@@ -44,5 +44,5 @@ interface ClockEvents {
fun onZenDataChanged(data: ZenData)
/** Update reactive axes for this clock */
- fun onFontAxesChanged(axes: List<ClockFontAxisSetting>)
+ fun onFontAxesChanged(axes: ClockAxisStyle)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
index f9ff75d5fdc8..5b67edda73cc 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
@@ -55,9 +55,9 @@ class ClockLogger(private val view: View?, buffer: MessageBuffer, tag: String) :
}
fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
- d({ "onLayout($bool1, ${VRect(long1.toULong())})" }) {
+ d({ "onLayout($bool1, ${VRect.fromLong(long1)})" }) {
bool1 = changed
- long1 = VRect(left, top, right, bottom).data.toLong()
+ long1 = VRect(left, top, right, bottom).toLong()
}
}
@@ -116,7 +116,7 @@ class ClockLogger(private val view: View?, buffer: MessageBuffer, tag: String) :
}
fun animateFidget(x: Float, y: Float) {
- d({ "animateFidget(${VPointF(long1.toULong())})" }) { long1 = VPointF(x, y).data.toLong() }
+ d({ "animateFidget(${VPointF.fromLong(long1)})" }) { long1 = VPointF(x, y).toLong() }
}
companion object {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
index 0cbc30d399d0..2147ca147b70 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
@@ -35,10 +35,54 @@ constructor(
/** Font axes that can be modified on this clock */
val axes: List<ClockFontAxis> = listOf(),
- /** List of font presets for this clock. Can be assigned directly. */
- val axisPresets: List<List<ClockFontAxisSetting>> = listOf(),
+ /** Presets for this clock. Null indicates the preset list should be disabled. */
+ val presetConfig: AxisPresetConfig? = null,
)
+data class AxisPresetConfig(
+ /** Groups of Presets. Each group can be used together in a single control. */
+ val groups: List<Group>,
+
+ /** Preset item currently being used, null when the current style is not a preset */
+ val current: IndexedStyle? = null,
+) {
+ /** The selected clock axis style, and its indices */
+ data class IndexedStyle(
+ /** Index of the group that this clock axis style appears in */
+ val groupIndex: Int,
+
+ /** Index of the preset within the group */
+ val presetIndex: Int,
+
+ /** Reference to the style in question */
+ val style: ClockAxisStyle,
+ )
+
+ /** A group of preset styles */
+ data class Group(
+ /* List of preset styles in this group */
+ val presets: List<ClockAxisStyle>,
+
+ /* Icon to use when this preset-group is active */
+ val icon: Drawable,
+ )
+
+ fun findStyle(style: ClockAxisStyle): IndexedStyle? {
+ groups.forEachIndexed { groupIndex, group ->
+ group.presets.forEachIndexed { presetIndex, preset ->
+ if (preset == style) {
+ return@findStyle IndexedStyle(
+ groupIndex = groupIndex,
+ presetIndex = presetIndex,
+ style = preset,
+ )
+ }
+ }
+ }
+ return null
+ }
+}
+
/** Represents an Axis that can be modified */
data class ClockFontAxis(
/** Axis key, not user renderable */
@@ -62,19 +106,12 @@ data class ClockFontAxis(
/** Description of the axis */
val description: String,
) {
- fun toSetting() = ClockFontAxisSetting(key, currentValue)
-
companion object {
- fun List<ClockFontAxis>.merge(
- axisSettings: List<ClockFontAxisSetting>
- ): List<ClockFontAxis> {
- val result = mutableListOf<ClockFontAxis>()
- for (axis in this) {
- val setting = axisSettings.firstOrNull { axis.key == it.key }
- val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis
- result.add(output)
- }
- return result
+ fun List<ClockFontAxis>.merge(axisStyle: ClockAxisStyle): List<ClockFontAxis> {
+ return this.map { axis ->
+ axisStyle.get(axis.key)?.let { axis.copy(currentValue = it) } ?: axis
+ }
+ .toList()
}
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
index e7b36626a810..cccc55835302 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
@@ -22,7 +22,7 @@ import org.json.JSONObject
data class ClockSettings(
val clockId: ClockId? = null,
val seedColor: Int? = null,
- val axes: List<ClockFontAxisSetting> = listOf(),
+ val axes: ClockAxisStyle = ClockAxisStyle(),
) {
// Exclude metadata from equality checks
var metadata: JSONObject = JSONObject()
@@ -38,15 +38,15 @@ data class ClockSettings(
put(KEY_CLOCK_ID, setting.clockId)
put(KEY_SEED_COLOR, setting.seedColor)
put(KEY_METADATA, setting.metadata)
- put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes))
+ put(KEY_AXIS_LIST, ClockAxisStyle.toJson(setting.axes))
}
}
fun fromJson(json: JSONObject): ClockSettings {
val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null
val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
- val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson)
- return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply {
+ val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockAxisStyle::fromJson)
+ return ClockSettings(clockId, seedColor, axisList ?: ClockAxisStyle()).apply {
metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject()
}
}
@@ -54,64 +54,102 @@ data class ClockSettings(
}
@Keep
-/** Axis setting value for a clock */
-data class ClockFontAxisSetting(
- /** Axis key; matches ClockFontAxis.key */
- val key: String,
+class ClockAxisStyle {
+ private val settings: MutableMap<String, Float>
- /** Value to set this axis to */
- val value: Float,
-) {
- companion object {
- private val KEY_AXIS_KEY = "key"
- private val KEY_AXIS_VALUE = "value"
+ // Iterable would be implemented on ClockAxisStyle directly,
+ // but that doesn't appear to work with plugins/dynamic libs.
+ val items: Iterable<Map.Entry<String, Float>>
+ get() = settings.asIterable()
- fun toJson(setting: ClockFontAxisSetting): JSONObject {
- return JSONObject().apply {
- put(KEY_AXIS_KEY, setting.key)
- put(KEY_AXIS_VALUE, setting.value)
- }
- }
+ val isEmpty: Boolean
+ get() = settings.isEmpty()
- fun toJson(settings: List<ClockFontAxisSetting>): JSONArray {
- return JSONArray().apply {
- for (axis in settings) {
- put(toJson(axis))
- }
- }
+ constructor(initialize: ClockAxisStyle.() -> Unit = {}) {
+ settings = mutableMapOf()
+ this.initialize()
+ }
+
+ constructor(style: ClockAxisStyle) {
+ settings = style.settings.toMutableMap()
+ }
+
+ constructor(items: Map<String, Float>) {
+ settings = items.toMutableMap()
+ }
+
+ constructor(key: String, value: Float) {
+ settings = mutableMapOf(key to value)
+ }
+
+ constructor(items: List<ClockFontAxis>) {
+ settings = items.associate { it.key to it.currentValue }.toMutableMap()
+ }
+
+ fun copy(initialize: ClockAxisStyle.() -> Unit): ClockAxisStyle {
+ return ClockAxisStyle(this).apply { initialize() }
+ }
+
+ operator fun get(key: String): Float? = settings[key]
+
+ operator fun set(key: String, value: Float) = put(key, value)
+
+ fun put(key: String, value: Float) {
+ settings.put(key, value)
+ }
+
+ fun toFVar(): String {
+ val sb = StringBuilder()
+ for (axis in settings) {
+ if (sb.length > 0) sb.append(", ")
+ sb.append("'${axis.key}' ${axis.value.toInt()}")
}
+ return sb.toString()
+ }
- fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting {
- return ClockFontAxisSetting(
- key = jsonObj.getString(KEY_AXIS_KEY),
- value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(),
- )
+ fun copyWith(replacements: ClockAxisStyle): ClockAxisStyle {
+ val result = ClockAxisStyle(this)
+ for ((key, value) in replacements.settings) {
+ result[key] = value
}
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ClockAxisStyle) return false
+ return settings == other.settings
+ }
- fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> {
- val result = mutableListOf<ClockFontAxisSetting>()
+ companion object {
+ private val KEY_AXIS_KEY = "key"
+ private val KEY_AXIS_VALUE = "value"
+
+ fun fromJson(jsonArray: JSONArray): ClockAxisStyle {
+ val result = ClockAxisStyle()
for (i in 0..jsonArray.length() - 1) {
val obj = jsonArray.getJSONObject(i)
if (obj == null) continue
- result.add(fromJson(obj))
+
+ result.put(
+ key = obj.getString(KEY_AXIS_KEY),
+ value = obj.getDouble(KEY_AXIS_VALUE).toFloat(),
+ )
}
return result
}
- fun List<ClockFontAxisSetting>.toFVar(): String {
- val sb = StringBuilder()
- for (axis in this) {
- if (sb.length > 0) sb.append(", ")
- sb.append("'${axis.key}' ${axis.value.toInt()}")
+ fun toJson(style: ClockAxisStyle): JSONArray {
+ return JSONArray().apply {
+ for ((key, value) in style.settings) {
+ put(
+ JSONObject().apply {
+ put(KEY_AXIS_KEY, key)
+ put(KEY_AXIS_VALUE, value)
+ }
+ )
+ }
}
- return sb.toString()
- }
-
- fun List<ClockFontAxisSetting>.replace(
- replacements: List<ClockFontAxisSetting>
- ): List<ClockFontAxisSetting> {
- var remaining = this.filterNot { lhs -> replacements.any { rhs -> lhs.key == rhs.key } }
- return remaining + replacements
}
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
index 1fb37ec28835..de62f9c8be74 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
@@ -56,6 +56,8 @@ value class VPointF(val data: ULong) {
fun toPointF() = PointF(x, y)
+ fun toLong(): Long = data.toLong()
+
fun lengthSq(): Float = x * x + y * y
fun length(): Float = sqrt(lengthSq())
@@ -110,6 +112,8 @@ value class VPointF(val data: ULong) {
companion object {
val ZERO = VPointF(0, 0)
+ fun fromLong(data: Long) = VPointF(data.toULong())
+
fun max(lhs: VPointF, rhs: VPointF) = VPointF(max(lhs.x, rhs.x), max(lhs.y, rhs.y))
fun min(lhs: VPointF, rhs: VPointF) = VPointF(min(lhs.x, rhs.x), min(lhs.y, rhs.y))
@@ -148,6 +152,8 @@ value class VPoint(val data: ULong) {
fun toPoint() = Point(x, y)
+ fun toLong(): Long = data.toLong()
+
fun abs() = VPoint(abs(x), abs(y))
operator fun component1(): Int = x
@@ -191,6 +197,8 @@ value class VPoint(val data: ULong) {
companion object {
val ZERO = VPoint(0, 0)
+ fun fromLong(data: Long) = VPoint(data.toULong())
+
fun max(lhs: VPoint, rhs: VPoint) = VPoint(max(lhs.x, rhs.x), max(lhs.y, rhs.y))
fun min(lhs: VPoint, rhs: VPoint) = VPoint(min(lhs.x, rhs.x), min(lhs.y, rhs.y))
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt
index 3c1adf22a405..1bd29aa6a073 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt
@@ -84,6 +84,10 @@ value class VRectF(val data: ULong) {
val size: VPointF
get() = VPointF(width, height)
+ fun toRectF(): RectF = RectF(left, top, right, bottom)
+
+ fun toLong(): Long = data.toLong()
+
override fun toString() = "($left, $top) -> ($right, $bottom)"
companion object {
@@ -91,6 +95,8 @@ value class VRectF(val data: ULong) {
private fun fromBits(value: Short): Float = Half.toFloat(Half.intBitsToHalf(value.toInt()))
+ fun fromLong(data: Long) = VRectF(data.toULong())
+
fun fromCenter(center: VPointF, size: VPointF): VRectF {
return VRectF(
center.x - size.x / 2,
@@ -162,11 +168,17 @@ value class VRect(val data: ULong) {
val size: VPoint
get() = VPoint(width, height)
+ fun toRect(): Rect = Rect(left, top, right, bottom)
+
+ fun toLong(): Long = data.toLong()
+
override fun toString() = "($left, $top) -> ($right, $bottom)"
companion object {
val ZERO = VRect(0, 0, 0, 0)
+ fun fromLong(data: Long) = VRect(data.toULong())
+
fun fromCenter(center: VPoint, size: VPoint): VRect {
return VRect(
(center.x - size.x / 2).toShort(),
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 124aec6a92dd..da2ec43e470c 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -52,7 +52,7 @@
android:gravity="center_vertical"
android:textSize="14sp" />
- <TextView
+ <com.android.systemui.util.DelayableMarqueeTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/bluetooth_device_summary"
@@ -60,7 +60,9 @@
android:paddingEnd="10dp"
android:paddingBottom="15dp"
android:maxLines="1"
- android:ellipsize="end"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
app:layout_constraintEnd_toStartOf="@+id/guideline"
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 949a6abb9b9d..a1b26fc9bb40 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -85,13 +85,49 @@
android:longClickable="false"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/input_routing_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/input_routing_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+ android:paddingStart="@dimen/hearing_devices_small_title_padding_horizontal"
+ android:text="@string/hearing_devices_input_routing_label"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:textSize="14sp"
+ android:gravity="center_vertical"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textDirection="locale"/>
+ <Spinner
+ android:id="@+id/input_routing_spinner"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:layout_marginTop="4dp"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
+ android:background="@drawable/hearing_devices_spinner_background"
+ android:popupBackground="@drawable/hearing_devices_spinner_popup_background"
+ android:dropDownWidth="match_parent"
+ android:longClickable="false"/>
+ </LinearLayout>
+
<com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout
android:id="@+id/ambient_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ app:layout_constraintTop_toBottomOf="@id/input_routing_layout"
android:layout_marginTop="@dimen/hearing_devices_layout_margin" />
<LinearLayout
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index 7b6916652924..fa852a2b8e85 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -18,6 +18,7 @@
<!-- extends LinearLayout -->
<com.android.systemui.statusbar.notification.row.NotificationInfo
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/notification_guts"
android:layout_width="match_parent"
@@ -324,18 +325,34 @@
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
</LinearLayout>
-
- <LinearLayout
+ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@*android:dimen/notification_2025_margin"
android:minHeight="@dimen/notification_2025_guts_button_size"
- android:gravity="center_vertical"
- >
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="@*android:dimen/notification_2025_margin"
+ app:layout_constraintStart_toStartOf="parent"
+ android:gravity="center"
+ android:minWidth="@dimen/notification_2025_min_tap_target_size"
+ android:minHeight="@dimen/notification_2025_min_tap_target_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"
+ android:textSize="@*android:dimen/notification_2025_action_text_size"
+ />
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
+ android:paddingStart="@dimen/notification_importance_button_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
@@ -345,6 +362,8 @@
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="200dp"
+ app:layout_constraintStart_toEndOf="@id/inline_dismiss"
+ app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
<TextView
@@ -354,12 +373,18 @@
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
- android:gravity="center"
+ android:gravity="end"
+ app:layout_constraintEnd_toEndOf="parent"
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="125dp"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
- </LinearLayout>
+ <androidx.constraintlayout.helper.widget.Flow
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:constraint_referenced_ids="inline_dismiss,turn_off_notifications,done"
+ app:flow_wrapMode="chain"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 089ceaee6ce3..d4bd142d9089 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -341,15 +341,28 @@ asked for it -->
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 4850b35833e5..d1755eff6dab 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -143,15 +143,28 @@
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 2e0a0ca1185c..3982a6638666 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -373,15 +373,28 @@ asked for it -->
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f4c6904028ca..15519ff37d0c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,7 +34,7 @@
<!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
to match the spec -->
- <color name="shade_panel_base">@android:color/system_accent1_900</color>
+ <color name="shade_panel_base">@android:color/system_accent1_100</color>
<color name="notification_scrim_base">@android:color/system_accent1_100</color>
<!-- Fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c1fd65d96d4..3fdb98b4e00b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1016,6 +1016,13 @@
<string name="hearing_devices_presets_error">Couldn\'t update preset</string>
<!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
<string name="hearing_devices_preset_label">Preset</string>
+ <!-- QuickSettings: Title for hearing aids input routing control in hearing device dialog. [CHAR LIMIT=40]-->
+ <string name="hearing_devices_input_routing_label">Default microphone for calls</string>
+ <!-- QuickSettings: Option for hearing aids input routing control in hearing device dialog. It will alter input routing for calls for hearing aid. [CHAR LIMIT=40]-->
+ <string-array name="hearing_device_input_routing_options">
+ <item>Hearing aid microphone</item>
+ <item>This phone\'s microphone</item>
+ </string-array>
<!-- QuickSettings: Content description for the icon that indicates the item is selected [CHAR LIMIT=NONE]-->
<string name="hearing_devices_spinner_item_selected">Selected</string>
<!-- QuickSettings: Title for ambient controls. [CHAR LIMIT=40]-->
@@ -2056,7 +2063,7 @@
<string name="inline_ok_button">Apply</string>
<!-- Notification inline controls: button to show block screen [CHAR_LIMIT=35] -->
- <string name="inline_turn_off_notifications">Turn off notifications</string>
+ <string name="inline_turn_off_notifications">Turn off</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_silence_title">Silent</string>
@@ -3181,8 +3188,6 @@
<string name="controls_media_active_session">The current media session cannot be hidden.</string>
<!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
<string name="controls_media_dismiss_button">Hide</string>
- <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
- <string name="controls_media_resume">Resume</string>
<!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
<string name="controls_media_settings_button">Settings</string>
<!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 763b1072f968..f2f177356fab 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -572,10 +572,6 @@ constructor(
}
fun handleFidgetTap(x: Float, y: Float) {
- if (!com.android.systemui.Flags.clockFidgetAnimation()) {
- return
- }
-
clock?.run {
smallClock.animations.onFidgetTap(x, y)
largeClock.animations.onFidgetTap(x, y)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d84b034eade8..60ec051315d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -124,7 +124,6 @@ import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -301,7 +300,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
private final Provider<SceneInteractor> mSceneInteractor;
private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
- private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
+ private final Provider<KeyguardServiceShowLockscreenInteractor>
+ mKeyguardServiceShowLockscreenInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2219,7 +2219,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
Provider<JavaAdapter> javaAdapter,
Provider<SceneInteractor> sceneInteractor,
Provider<CommunalSceneInteractor> communalSceneInteractor,
- KeyguardServiceShowLockscreenInteractor keyguardServiceShowLockscreenInteractor) {
+ Provider<KeyguardServiceShowLockscreenInteractor>
+ keyguardServiceShowLockscreenInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2553,7 +2554,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
if (KeyguardWmStateRefactor.isEnabled()) {
mJavaAdapter.get().alwaysCollectFlow(
- mKeyguardServiceShowLockscreenInteractor.getShowNowEvents(),
+ mKeyguardServiceShowLockscreenInteractor.get().getShowNowEvents(),
this::onKeyguardServiceShowLockscreenNowEvents
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c78f75a334fd..089466707298 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -894,8 +894,8 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
if (NotificationBundleUi.isEnabled()) {
return enr.getEntryAdapter().canDragAndDrop();
} else {
- boolean canBubble = enr.getEntry().canBubble();
- Notification notif = enr.getEntry().getSbn().getNotification();
+ boolean canBubble = enr.getEntryLegacy().canBubble();
+ Notification notif = enr.getEntryLegacy().getSbn().getNotification();
PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
: notif.fullScreenIntent;
if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index fb3bc620ee68..14b13d105482 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -121,6 +121,13 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
private Spinner mPresetSpinner;
private HearingDevicesPresetsController mPresetController;
private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
+
+ private View mInputRoutingLayout;
+ private Spinner mInputRoutingSpinner;
+ private HearingDevicesInputRoutingController.Factory mInputRoutingControllerFactory;
+ private HearingDevicesInputRoutingController mInputRoutingController;
+ private HearingDevicesSpinnerAdapter mInputRoutingAdapter;
+
private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
new HearingDevicesPresetsController.PresetCallback() {
@Override
@@ -169,7 +176,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
@Background Executor bgExecutor,
AudioManager audioManager,
HearingDevicesUiEventLogger uiEventLogger,
- QSSettingsPackageRepository qsSettingsPackageRepository) {
+ QSSettingsPackageRepository qsSettingsPackageRepository,
+ HearingDevicesInputRoutingController.Factory inputRoutingControllerFactory) {
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
mActivityStarter = activityStarter;
@@ -182,6 +190,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mUiEventLogger = uiEventLogger;
mLaunchSourceId = launchSourceId;
mQSSettingsPackageRepository = qsSettingsPackageRepository;
+ mInputRoutingControllerFactory = inputRoutingControllerFactory;
}
@Override
@@ -239,6 +248,12 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mPresetLayout.setVisibility(
mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
}
+ if (mInputRoutingController != null) {
+ mInputRoutingController.setDevice(device);
+ mInputRoutingController.isInputRoutingControlAvailable(
+ available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+ available ? VISIBLE : GONE)));
+ }
if (mAmbientController != null) {
mAmbientController.loadDevice(device);
}
@@ -310,6 +325,11 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
setupDeviceListView(dialog, hearingDeviceItemList);
setupPairNewDeviceButton(dialog);
setupPresetSpinner(dialog, activeHearingDevice);
+ if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()
+ && com.android.systemui.Flags
+ .hearingDevicesInputRoutingUiImprovement()) {
+ setupInputRoutingSpinner(dialog, activeHearingDevice);
+ }
if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
setupAmbientControls(activeHearingDevice);
}
@@ -383,6 +403,52 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mBgExecutor.execute(() -> mPresetController.registerHapCallback());
}
+ private void setupInputRoutingSpinner(SystemUIDialog dialog,
+ CachedBluetoothDevice activeHearingDevice) {
+ mInputRoutingController = mInputRoutingControllerFactory.create(dialog.getContext());
+ mInputRoutingController.setDevice(activeHearingDevice);
+
+ mInputRoutingSpinner = dialog.requireViewById(R.id.input_routing_spinner);
+ mInputRoutingAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
+ mInputRoutingAdapter.addAll(
+ HearingDevicesInputRoutingController.getInputRoutingOptions(dialog.getContext()));
+ mInputRoutingSpinner.setAdapter(mInputRoutingAdapter);
+ // Disable redundant Touch & Hold accessibility action for Switch Access
+ mInputRoutingSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+ // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+ // to avoid extra onItemSelected() get called when first register the listener.
+ final int initialPosition =
+ mInputRoutingController.getUserPreferredInputRoutingValue();
+ mInputRoutingSpinner.setSelection(initialPosition, false);
+ mInputRoutingAdapter.setSelected(initialPosition);
+ mInputRoutingSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mInputRoutingAdapter.setSelected(position);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_INPUT_ROUTING_SELECT,
+ mLaunchSourceId);
+ mInputRoutingController.selectInputRouting(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing
+ }
+ });
+
+ mInputRoutingLayout = dialog.requireViewById(R.id.input_routing_layout);
+ mInputRoutingController.isInputRoutingControlAvailable(
+ available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+ available ? VISIBLE : GONE)));
+ }
+
private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
new file mode 100644
index 000000000000..e8fa7ba2268b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.content.Context
+import android.media.AudioManager
+import android.util.Log
+import androidx.collection.ArraySet
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * The controller of the hearing device input routing.
+ *
+ * <p> It manages and update the input routing according to the value.
+ */
+open class HearingDevicesInputRoutingController
+@AssistedInject
+constructor(
+ @Assisted context: Context,
+ private val audioManager: AudioManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ private val audioRoutingHelper = HearingAidAudioRoutingHelper(context)
+ private var cachedDevice: CachedBluetoothDevice? = null
+ private val bgCoroutineScope = CoroutineScope(backgroundDispatcher)
+
+ /** Factory to create a [HearingDevicesInputRoutingController] instance. */
+ @AssistedFactory
+ interface Factory {
+ fun create(context: Context): HearingDevicesInputRoutingController
+ }
+
+ /** Possible input routing UI. Need to align with [getInputRoutingOptions] */
+ enum class InputRoutingValue {
+ HEARING_DEVICE,
+ BUILTIN_MIC,
+ }
+
+ companion object {
+ private const val TAG = "HearingDevicesInputRoutingController"
+
+ /** Gets input routing options as strings. */
+ @JvmStatic
+ fun getInputRoutingOptions(context: Context): Array<String> {
+ return context.resources.getStringArray(R.array.hearing_device_input_routing_options)
+ }
+ }
+
+ fun interface InputRoutingControlAvailableCallback {
+ fun onResult(available: Boolean)
+ }
+
+ /**
+ * Sets the device for this controller to control the input routing.
+ *
+ * @param device the [CachedBluetoothDevice] set to the controller
+ */
+ fun setDevice(device: CachedBluetoothDevice?) {
+ this@HearingDevicesInputRoutingController.cachedDevice = device
+ }
+
+ fun isInputRoutingControlAvailable(callback: InputRoutingControlAvailableCallback) {
+ bgCoroutineScope.launch {
+ val result = isInputRoutingControlAvailableInternal()
+ callback.onResult(result)
+ }
+ }
+
+ /**
+ * Checks if input routing control is available for the currently set device.
+ *
+ * @return `true` if input routing control is available.
+ */
+ private suspend fun isInputRoutingControlAvailableInternal(): Boolean {
+ val device = cachedDevice ?: return false
+
+ val memberDevices = device.memberDevice
+
+ val inputInfos =
+ withContext(backgroundDispatcher) {
+ audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
+ }
+ val supportedInputDeviceAddresses = ArraySet<String>()
+ supportedInputDeviceAddresses.add(device.address)
+ if (memberDevices.isNotEmpty()) {
+ memberDevices.forEach { supportedInputDeviceAddresses.add(it.address) }
+ }
+
+ val isValidInputDevice =
+ inputInfos.any { supportedInputDeviceAddresses.contains(it.address) }
+ // Not support ASHA hearing device for input routing feature
+ val isHapHearingDevice = device.profiles.any { profile -> profile is HapClientProfile }
+
+ if (isHapHearingDevice && !isValidInputDevice) {
+ Log.d(TAG, "Not supported input type hearing device.")
+ }
+ return isHapHearingDevice && isValidInputDevice
+ }
+
+ /** Gets the user's preferred [InputRoutingValue]. */
+ fun getUserPreferredInputRoutingValue(): Int {
+ val device = cachedDevice ?: return InputRoutingValue.HEARING_DEVICE.ordinal
+
+ return if (device.device.isMicrophonePreferredForCalls) {
+ InputRoutingValue.HEARING_DEVICE.ordinal
+ } else {
+ InputRoutingValue.BUILTIN_MIC.ordinal
+ }
+ }
+
+ /**
+ * Sets the input routing to [android.bluetooth.BluetoothDevice.setMicrophonePreferredForCalls]
+ * based on the input routing index.
+ *
+ * @param inputRoutingIndex The desired input routing index.
+ */
+ fun selectInputRouting(inputRoutingIndex: Int) {
+ val device = cachedDevice ?: return
+
+ val useBuiltinMic = (inputRoutingIndex == InputRoutingValue.BUILTIN_MIC.ordinal)
+ val status =
+ audioRoutingHelper.setPreferredInputDeviceForCalls(
+ device,
+ if (useBuiltinMic) HearingAidAudioRoutingConstants.RoutingValue.BUILTIN_DEVICE
+ else HearingAidAudioRoutingConstants.RoutingValue.AUTO,
+ )
+ if (!status) {
+ Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls")
+ }
+ setMicrophonePreferredForCallsForDeviceSet(device, !useBuiltinMic)
+ }
+
+ private fun setMicrophonePreferredForCallsForDeviceSet(
+ device: CachedBluetoothDevice?,
+ enabled: Boolean,
+ ) {
+ device ?: return
+ device.device.isMicrophonePreferredForCalls = enabled
+ val memberDevices = device.memberDevice
+ if (memberDevices.isNotEmpty()) {
+ memberDevices.forEach { d -> d.device.isMicrophonePreferredForCalls = enabled }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 4a695d638713..82ac10d85e70 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -40,6 +40,8 @@ enum class HearingDevicesUiEvent(private val id: Int) : UiEventLogger.UiEventEnu
HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
@UiEvent(doc = "Collapse the ambient volume controls")
HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154),
+ @UiEvent(doc = "Select a input routing option from input routing spinner")
+ HEARING_DEVICES_INPUT_ROUTING_SELECT(2155),
@UiEvent(doc = "Click on the device settings to enter hearing devices page")
HEARING_DEVICES_SETTINGS_CLICK(2172);
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
index 5a5a51e53d63..8d066bbaa915 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
@@ -419,6 +419,8 @@ constructor(
}
nameView.text = item.deviceName
summaryView.text = item.connectionSummary
+ // needed for marquee
+ summaryView.isSelected = true
actionIconView.setOnClickListener {
mutableDeviceItemClick.value =
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
index df6c1b18e3e9..8cebe04d4e01 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -24,11 +24,17 @@ import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
+import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@SysUISingleton
-class ActionIntentCreator @Inject constructor() : IntentCreator {
+class ActionIntentCreator
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator {
override fun getTextEditorIntent(context: Context?) =
Intent(context, EditTextActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -65,7 +71,7 @@ class ActionIntentCreator @Inject constructor() : IntentCreator {
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
- override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+ suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent {
val editorPackage = context.getString(R.string.config_screenshotEditor)
return Intent(Intent.ACTION_EDIT).apply {
if (!TextUtils.isEmpty(editorPackage)) {
@@ -78,6 +84,14 @@ class ActionIntentCreator @Inject constructor() : IntentCreator {
}
}
+ override fun getImageEditIntentAsync(
+ uri: Uri?,
+ context: Context,
+ outputConsumer: Consumer<Intent>,
+ ) {
+ applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) }
+ }
+
override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
return Intent(REMOTE_COPY_ACTION).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 314b6e7f5a28..984d2478eb72 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -558,8 +558,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
- animateOut();
+ mIntentCreator.getImageEditIntentAsync(uri, mContext, intent -> {
+ mContext.startActivity(intent);
+ animateOut();
+ });
}
private void editText() {
@@ -747,8 +749,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
mIntentCreator.getTextEditorIntent(mContext));
break;
case IMAGE:
- finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+ mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext,
+ intent -> {
+ finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent);
+ });
break;
default:
Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
index 4b24536ad28f..e9a9cbf106fc 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -27,6 +27,8 @@ import android.text.TextUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
@SysUISingleton
@@ -73,7 +75,7 @@ public class DefaultIntentCreator implements IntentCreator {
return chooserIntent;
}
- public Intent getImageEditIntent(Uri uri, Context context) {
+ public void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer) {
String editorPackage = context.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -83,7 +85,7 @@ public class DefaultIntentCreator implements IntentCreator {
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
- return editIntent;
+ outputConsumer.accept(editIntent);
}
public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index c8a6b05f090b..283596f9f309 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -21,9 +21,11 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import java.util.function.Consumer;
+
public interface IntentCreator {
Intent getTextEditorIntent(Context context);
Intent getShareIntent(ClipData clipData, Context context);
- Intent getImageEditIntent(Uri uri, Context context);
+ void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer);
Intent getRemoteCopyIntent(ClipData clipData, Context context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 857fa5cac3e2..756edb3d048d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -247,7 +247,7 @@ constructor(
showsOnlyActiveMedia = false
}
falsingProtectionNeeded = false
- disablePagination = true
+ disableScrolling = true
init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 06a14eaa5c85..440c3001a2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -101,6 +102,7 @@ constructor(
val (_, isActive) = withPrev
// The validation is performed once the hub becomes active.
if (isActive) {
+ removeNotLockscreenWidgets(widgets)
validateWidgetsAndDeleteOrphaned(widgets)
}
}
@@ -144,6 +146,19 @@ constructor(
}
}
+ private fun removeNotLockscreenWidgets(widgets: List<CommunalWidgetContentModel>) {
+ widgets
+ .filter { widget ->
+ when (widget) {
+ is CommunalWidgetContentModel.Available ->
+ widget.providerInfo.widgetCategory and
+ AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD != 0
+ else -> false
+ }
+ }
+ .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+ }
+
/**
* Ensure the existence of all associated users for widgets, and remove widgets belonging to
* users who have been deleted.
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 9b181be93b61..f3316958f01d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,8 +16,13 @@
package com.android.systemui.display
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.createDisplayLibComponent
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.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
import com.android.systemui.display.data.repository.DisplayRepository
@@ -28,6 +33,8 @@ import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepos
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
import com.android.systemui.display.data.repository.FocusedDisplayRepository
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
+import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -40,9 +47,11 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
/** Module binding display related classes. */
-@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class, DisplayLibModule::class])
interface DisplayModule {
@Binds
fun bindConnectedDisplayInteractor(
@@ -73,6 +82,9 @@ interface DisplayModule {
impl: DisplayWindowPropertiesRepositoryImpl
): DisplayWindowPropertiesRepository
+ @Binds
+ fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
+
companion object {
@Provides
@SysUISingleton
@@ -103,3 +115,31 @@ interface DisplayModule {
}
}
}
+
+/** Module to bind the DisplayRepository from displaylib to the systemui dagger graph. */
+@Module
+object DisplayLibModule {
+ @Provides
+ @SysUISingleton
+ fun displayLibComponent(
+ displayManager: DisplayManager,
+ @Background backgroundHandler: Handler,
+ @Background bgApplicationScope: CoroutineScope,
+ @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+ ): DisplayLibComponent {
+ return createDisplayLibComponent(
+ displayManager,
+ backgroundHandler,
+ bgApplicationScope,
+ backgroundCoroutineDispatcher,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesDisplayRepositoryFromLib(
+ displayLibComponent: DisplayLibComponent
+ ): com.android.app.displaylib.DisplayRepository {
+ return displayLibComponent.displayRepository
+ }
+}
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 721d116004f3..051fe7e5517c 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
@@ -17,112 +17,30 @@
package com.android.systemui.display.data.repository
import android.annotation.SuppressLint
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
-import android.hardware.display.DisplayManager.DisplayListener
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
-import android.os.Handler
-import android.util.Log
-import android.view.Display
import android.view.IWindowManager
-import com.android.app.tracing.FlowTracing.traceEach
-import com.android.app.tracing.traceSection
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Compile
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
/** Repository for providing access to display related information and events. */
-interface DisplayRepository {
- /** Display change event indicating a change to the given displayId has occurred. */
- val displayChangeEvent: Flow<Int>
-
- /** Display addition event indicating a new display has been added. */
- val displayAdditionEvent: Flow<Display?>
-
- /** Display removal event indicating a display has been removed. */
- val displayRemovalEvent: Flow<Int>
+interface DisplayRepository : DisplayRepositoryFromLib {
/** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-
- /**
- * Provides the current set of displays.
- *
- * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
- */
- val displays: StateFlow<Set<Display>>
-
- /**
- * Provides the current set of display ids.
- *
- * Note that it is preferred to use this instead of [displays] if only the
- * [Display.getDisplayId] is needed.
- */
- val displayIds: StateFlow<Set<Int>>
-
- /**
- * Pending display id that can be enabled/disabled.
- *
- * When `null`, it means there is no pending display waiting to be enabled.
- */
- val pendingDisplay: Flow<PendingDisplay?>
-
- /** Whether the default display is currently off. */
- val defaultDisplayOff: Flow<Boolean>
-
- /**
- * Given a display ID int, return the corresponding Display object, or null if none exist.
- *
- * This method is guaranteed to not result in any binder call.
- */
- fun getDisplay(displayId: Int): Display? =
- displays.value.firstOrNull { it.displayId == displayId }
-
- /** Represents a connected display that has not been enabled yet. */
- interface PendingDisplay {
- /** Id of the pending display. */
- val id: Int
-
- /** Enables the display, making it available to the system. */
- suspend fun enable()
-
- /**
- * Ignores the pending display. When called, this specific display id doesn't appear as
- * pending anymore until the display is disconnected and reconnected again.
- */
- suspend fun ignore()
-
- /** Disables the display, making it unavailable to the system. */
- suspend fun disable()
- }
}
@SysUISingleton
@@ -130,310 +48,11 @@ interface DisplayRepository {
class DisplayRepositoryImpl
@Inject
constructor(
- private val displayManager: DisplayManager,
private val commandQueue: CommandQueue,
private val windowManager: IWindowManager,
- @Background backgroundHandler: Handler,
@Background bgApplicationScope: CoroutineScope,
- @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
-) : DisplayRepository {
- private val allDisplayEvents: Flow<DisplayEvent> =
- conflatedCallbackFlow {
- val callback =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- trySend(DisplayEvent.Added(displayId))
- }
-
- override fun onDisplayRemoved(displayId: Int) {
- trySend(DisplayEvent.Removed(displayId))
- }
-
- override fun onDisplayChanged(displayId: Int) {
- trySend(DisplayEvent.Changed(displayId))
- }
- }
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- EVENT_TYPE_DISPLAY_ADDED or
- EVENT_TYPE_DISPLAY_CHANGED or
- EVENT_TYPE_DISPLAY_REMOVED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
- .debugLog("allDisplayEvents")
- .flowOn(backgroundCoroutineDispatcher)
-
- override val displayChangeEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
-
- override val displayRemovalEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
-
- // This is necessary because there might be multiple displays, and we could
- // have missed events for those added before this process or flow started.
- // Note it causes a binder call from the main thread (it's traced).
- private val initialDisplays: Set<Display> =
- traceSection("$TAG#initialDisplays") { displayManager.displays?.toSet() ?: emptySet() }
- private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
-
- /** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: StateFlow<Set<Int>> =
- allDisplayEvents
- .scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
- val id = event.displayId
- when (event) {
- is DisplayEvent.Removed -> previousIds - id
- is DisplayEvent.Added,
- is DisplayEvent.Changed -> previousIds + id
- }
- }
- .distinctUntilChanged()
- .debugLog("enabledDisplayIds")
- .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
-
- private val defaultDisplay by lazy {
- getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
- ?: error("Unable to get default display.")
- }
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- private val enabledDisplays: StateFlow<Set<Display>> =
- enabledDisplayIds
- .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
- .onEach {
- if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("enabledDisplays")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // This triggers a single binder call on the UI thread per process. The
- // alternative would be to use sharedFlows, but they are prohibited due to
- // performance concerns.
- // Ultimately, this is a trade-off between a one-time UI thread binder call and
- // the constant overhead of sharedFlows.
- initialValue = initialDisplays,
- )
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- override val displays: StateFlow<Set<Display>> = enabledDisplays
-
- override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
-
- /**
- * 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")
-
- private fun getInitialConnectedDisplays(): Set<Int> =
- traceSection("$TAG#getInitialConnectedDisplays") {
- displayManager
- .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
- .map { it.displayId }
- .toSet()
- .also {
- if (DEBUG) {
- Log.d(TAG, "getInitialConnectedDisplays: $it")
- }
- }
- }
-
- /* keeps connected displays until they are disconnected. */
- private val connectedDisplayIds: StateFlow<Set<Int>> =
- conflatedCallbackFlow {
- val connectedIds = getInitialConnectedDisplays().toMutableSet()
- val callback =
- object : DisplayConnectionListener {
- override fun onDisplayConnected(id: Int) {
- if (DEBUG) {
- Log.d(TAG, "display with id=$id connected.")
- }
- connectedIds += id
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
-
- override fun onDisplayDisconnected(id: Int) {
- connectedIds -= id
- if (DEBUG) {
- Log.d(TAG, "display with id=$id disconnected.")
- }
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
- }
- trySend(connectedIds.toSet())
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- /* eventFlags */ 0,
- DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .distinctUntilChanged()
- .debugLog("connectedDisplayIds")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // The initial value is set to empty, but connected displays are gathered as soon as
- // the flow starts being collected. This is to ensure the call to get displays (an
- // IPC) happens in the background instead of when this object
- // is instantiated.
- initialValue = emptySet(),
- )
-
- private val connectedExternalDisplayIds: Flow<Set<Int>> =
- connectedDisplayIds
- .map { connectedDisplayIds ->
- traceSection("$TAG#filteringExternalDisplays") {
- connectedDisplayIds
- .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
- .toSet()
- }
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("connectedExternalDisplayIds")
-
- private fun getDisplayType(displayId: Int): Int? =
- traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
-
- private fun getDisplayFromDisplayManager(displayId: Int): Display? =
- traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
-
- /**
- * Pending displays are the ones connected, but not enabled and not ignored.
- *
- * A connected display is ignored after the user makes the decision to use it or not. For now,
- * the initial decision from the user is final and not reversible.
- */
- private val pendingDisplayIds: Flow<Set<Int>> =
- combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
- enabledDisplaysIds,
- connectedExternalDisplayIds,
- ignoredDisplayIds ->
- if (DEBUG) {
- Log.d(
- TAG,
- "combining enabled=$enabledDisplaysIds, " +
- "connectedExternalDisplayIds=$connectedExternalDisplayIds, " +
- "ignored=$ignoredDisplayIds",
- )
- }
- connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
- }
- .debugLog("allPendingDisplayIds")
-
- /** Which display id should be enabled among the pending ones. */
- private val pendingDisplayId: Flow<Int?> =
- pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
-
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
- pendingDisplayId
- .map { displayId ->
- val id = displayId ?: return@map null
- object : DisplayRepository.PendingDisplay {
- override val id = id
-
- override suspend fun enable() {
- traceSection("DisplayRepository#enable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Enabling display with id=$id")
- }
- displayManager.enableConnectedDisplay(id)
- }
- // After the display has been enabled, it is automatically ignored.
- ignore()
- }
-
- override suspend fun ignore() {
- traceSection("DisplayRepository#ignore($id)") {
- _ignoredDisplayIds.value += id
- }
- }
-
- override suspend fun disable() {
- ignore()
- traceSection("DisplayRepository#disable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Disabling display with id=$id")
- }
- displayManager.disableConnectedDisplay(id)
- }
- }
- }
- }
- .debugLog("pendingDisplay")
-
- override val defaultDisplayOff: Flow<Boolean> =
- displayChangeEvent
- .filter { it == Display.DEFAULT_DISPLAY }
- .map { defaultDisplay.state == Display.STATE_OFF }
- .distinctUntilChanged()
-
- private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
- return if (DEBUG) {
- traceEach(flowName, logcat = true, traceEmissionCount = true)
- } else {
- this
- }
- }
-
- /**
- * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
- * account the diff between each root flow emission.
- *
- * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
- * that if the [createValue] returns a null element, it will not be added in the output set.
- */
- private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
- data class State<T, V>(
- val previousSet: Set<T>,
- // Caches T values from the previousSet that were already converted to V
- val valueMap: Map<T, V>,
- val resultSet: Set<V>,
- )
-
- val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
- return this.scan(emptyInitialState) { state, currentSet ->
- if (currentSet == state.previousSet) {
- state
- } else {
- val removed = state.previousSet - currentSet
- val added = currentSet - state.previousSet
- val newMap = state.valueMap.toMutableMap()
-
- added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
- removed.forEach { key -> newMap.remove(key) }
-
- val resultSet = newMap.values.toSet()
- State(currentSet, newMap, resultSet)
- }
- }
- .filter { it != emptyInitialState }
- .map { it.resultSet }
- }
+ private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
+) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
private val decorationEvents: Flow<Event> = callbackFlow {
val callback =
@@ -487,20 +106,5 @@ constructor(
private companion object {
const val TAG = "DisplayRepository"
- val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
}
}
-
-/** Used to provide default implementations for all methods. */
-private interface DisplayConnectionListener : DisplayListener {
-
- override fun onDisplayConnected(id: Int) {}
-
- override fun onDisplayDisconnected(id: Int) {}
-
- override fun onDisplayAdded(id: Int) {}
-
- override fun onDisplayRemoved(id: Int) {}
-
- override fun onDisplayChanged(id: Int) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
new file mode 100644
index 000000000000..212d55612935
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpableFromToString
+import javax.inject.Inject
+
+/** Helper class to register PerDisplayRepository in the dump manager in SystemUI. */
+@SysUISingleton
+class PerDisplayRepoDumpHelper @Inject constructor(private val dumpManager: DumpManager) :
+ PerDisplayRepository.InitCallback {
+ /**
+ * Registers PerDisplayRepository in the dump manager.
+ *
+ * The repository will be identified by the given debug name.
+ */
+ override fun onInit(debugName: String, instance: Any) {
+ dumpManager.registerNormalDumpable(
+ "PerDisplayRepository-$debugName",
+ DumpableFromToString(instance),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
index d1d013542fbf..7e00c60dc43a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
@@ -20,13 +20,10 @@ import android.util.Log
import android.view.Display
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
-import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dump.DumpManager
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
@@ -88,6 +85,20 @@ interface PerDisplayRepository<T> {
/** Debug name for this repository, mainly for tracing and logging. */
val debugName: String
+
+ /**
+ * Callback to run when a given repository is initialized.
+ *
+ * This allows the caller to perform custom logic when the repository is ready to be used, e.g.
+ * register to dumpManager.
+ *
+ * Note that the instance is *leaked* outside of this class, so it should only be done when
+ * repository is meant to live as long as the caller. In systemUI this is ok because the
+ * repository lives as long as the process itself.
+ */
+ interface InitCallback {
+ fun onInit(debugName: String, instance: Any)
+ }
}
/**
@@ -110,8 +121,8 @@ constructor(
@Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
@Background private val backgroundApplicationScope: CoroutineScope,
private val displayRepository: DisplayRepository,
- private val dumpManager: DumpManager,
-) : PerDisplayRepository<T>, Dumpable {
+ private val initCallback: PerDisplayRepository.InitCallback,
+) : PerDisplayRepository<T> {
private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
@@ -120,7 +131,7 @@ constructor(
}
private suspend fun start() {
- dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
+ initCallback.onInit(debugName, this)
displayRepository.displayIds.collectLatest { displayIds ->
val toRemove = perDisplayInstances.keys - displayIds
toRemove.forEach { displayId ->
@@ -169,8 +180,9 @@ constructor(
private const val TAG = "PerDisplayInstanceRepo"
}
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println(perDisplayInstances)
+ override fun toString(): String {
+ return "PerDisplayInstanceRepositoryImpl(" +
+ "debugName='$debugName', instances=$perDisplayInstances)"
}
}
@@ -193,6 +205,7 @@ class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
private val lazyDefaultDisplayInstance by lazy {
instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
}
+
override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 15a3cbdb8072..84f103e8f730 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.display.domain.interactor
import android.companion.virtual.VirtualDeviceManager
import android.view.Display
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -138,7 +139,8 @@ constructor(
.distinctUntilChanged()
.flowOn(backgroundCoroutineDispatcher)
- private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
+ private fun DisplayRepositoryFromLib.PendingDisplay.toInteractorPendingDisplay():
+ PendingDisplay =
object : PendingDisplay {
override suspend fun enable() = this@toInteractorPendingDisplay.enable()
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
new file mode 100644
index 000000000000..438931a1962d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dump
+
+import com.android.systemui.Dumpable
+import java.io.PrintWriter
+
+/** Dumpable implementation that just calls toString() on the instance. */
+class DumpableFromToString<T>(private val instance: T) : Dumpable {
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("$instance")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index cf5c3402792e..f2a10cc43fd9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -147,14 +147,14 @@ import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
* on whether the keyguard is showing, and whether the device is provisioned.
@@ -270,6 +270,16 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final UserLogoutInteractor mLogoutInteractor;
private final GlobalActionsInteractor mInteractor;
private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
+ private final Handler mHandler;
+
+ private final UserTracker.Callback mOnUserSwitched = new UserTracker.Callback() {
+ @Override
+ public void onBeforeUserSwitching(int newUser) {
+ // Dismiss the dialog as soon as we start switching. This will schedule a message
+ // in a handler so it will be pretty quick.
+ dismissDialog();
+ }
+ };
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -425,6 +435,29 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mInteractor = interactor;
mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
+ mHandler = new Handler(mMainHandler.getLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_DISMISS:
+ if (mDialog != null) {
+ if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
+ } else {
+ mDialog.dismiss();
+ }
+ mDialog = null;
+ }
+ break;
+ case MESSAGE_REFRESH:
+ refreshSilentMode();
+ mAdapter.notifyDataSetChanged();
+ break;
+ }
+ }
+ };
+
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -537,6 +570,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
expandable != null ? expandable.dialogTransitionController(
new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
INTERACTION_JANK_TAG)) : null;
+ mUserTracker.addCallback(mOnUserSwitched, mBackgroundExecutor);
if (controller != null) {
mDialogTransitionAnimator.show(mDialog, controller);
} else {
@@ -1404,6 +1438,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mInteractor.onDismissed();
+ mUserTracker.removeCallback(mOnUserSwitched);
}
/**
@@ -2228,29 +2263,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialogPressDelay = 0; // ms
}
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_DISMISS:
- if (mDialog != null) {
- if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- // Hide instantly.
- mDialog.hide();
- mDialog.dismiss();
- } else {
- mDialog.dismiss();
- }
- mDialog = null;
- }
- break;
- case MESSAGE_REFRESH:
- refreshSilentMode();
- mAdapter.notifyDataSetChanged();
- break;
- }
- }
- };
-
private void onAirplaneModeChanged() {
// Let the service state callbacks handle the state.
if (mHasTelephony || mAirplaneModeOn == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 757464976261..79685088fed7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -673,7 +673,8 @@ public class KeyguardService extends Service {
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
} else if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceDoKeyguardTimeout();
+ mKeyguardServiceShowLockscreenInteractor
+ .onKeyguardServiceDoKeyguardTimeout(options);
}
mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d8fc21af9724..4755e2845587 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -134,7 +134,6 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
-import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.TransitionAnimator;
@@ -194,7 +193,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -4129,14 +4127,14 @@ public class KeyguardViewMediator implements CoreStartable,
private void notifyLockNowCallback() {
List<LockNowCallback> callbacks;
+
synchronized (mLockNowCallbacks) {
- callbacks = new ArrayList<LockNowCallback>(mLockNowCallbacks);
+ callbacks = new ArrayList<>(mLockNowCallbacks);
mLockNowCallbacks.clear();
}
- Iterator<LockNowCallback> iter = callbacks.listIterator();
- while (iter.hasNext()) {
- LockNowCallback callback = iter.next();
- iter.remove();
+
+ for (int i = 0; i < callbacks.size(); i++) {
+ final LockNowCallback callback = callbacks.get(i);
if (callback.mUserId != mSelectedUserInteractor.getSelectedUserId()) {
Log.i(TAG, "Not notifying lockNowCallback due to user mismatch");
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 58692746d1e0..51b953ef290c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -22,11 +22,14 @@ import android.util.Log
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
import android.view.WindowManager
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.window.flags.Flags
import com.android.wm.shell.keyguard.KeyguardTransitions
import java.util.concurrent.Executor
@@ -46,6 +49,9 @@ constructor(
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
private val keyguardTransitions: KeyguardTransitions,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor,
) {
/**
@@ -92,12 +98,23 @@ constructor(
* second timeout).
*/
private var isKeyguardGoingAway = false
- private set(value) {
+ private set(goingAway) {
// TODO(b/278086361): Extricate the keyguard state controller.
- keyguardStateController.notifyKeyguardGoingAway(value)
- field = value
+ keyguardStateController.notifyKeyguardGoingAway(goingAway)
+
+ if (goingAway) {
+ keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId()
+ }
+
+ field = goingAway
}
+ /**
+ * The current user ID when we asked WM to start the keyguard going away animation. This is used
+ * for validation when user switching occurs during unlock.
+ */
+ private var keyguardGoingAwayRequestedForUserId: Int = -1
+
/** Callback provided by WM to call once we're done with the going away animation. */
private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
@@ -171,6 +188,14 @@ constructor(
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback,
) {
+ goingAwayRemoteAnimationFinishedCallback = finishedCallback
+
+ if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) {
+ Log.d(TAG, "User switched during keyguard going away - ending remote animation.")
+ endKeyguardGoingAwayAnimation()
+ return
+ }
+
// If we weren't expecting the keyguard to be going away, WM triggered this transition.
if (!isKeyguardGoingAway) {
// Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
@@ -198,7 +223,6 @@ constructor(
}
if (apps.isNotEmpty()) {
- goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
// Nothing to do here if we have no apps, end the animation, which will cancel it and WM
@@ -211,6 +235,7 @@ constructor(
// If WM cancelled the animation, we need to end immediately even if we're still using the
// animation.
endKeyguardGoingAwayAnimation()
+ maybeStartTransitionIfUserSwitchedDuringGoingAway()
}
/**
@@ -301,6 +326,29 @@ constructor(
}
}
+ /**
+ * If necessary, start a transition to show/hide keyguard in response to a user switch during
+ * keyguard going away.
+ *
+ * Returns [true] if a transition was started, or false if a transition was not necessary.
+ */
+ private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean {
+ val currentUser = selectedUserInteractor.getSelectedUserId()
+ if (currentUser != keyguardGoingAwayRequestedForUserId) {
+ if (lockPatternUtils.isSecure(currentUser)) {
+ keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway()
+ } else {
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+ reason = "User switch during keyguard going away, and new user is insecure"
+ )
+ }
+
+ return true
+ } else {
+ return false
+ }
+ }
+
companion object {
private val TAG = "WindowManagerLsVis"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
new file mode 100644
index 000000000000..16c2d14b78ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.os.IRemoteCallback
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Holds an IRemoteCallback along with the current user ID at the time the callback was provided.
+ */
+data class ShowLockscreenCallback(val userId: Int, val remoteCallback: IRemoteCallback)
+
+/** Maintains state related to KeyguardService requests to show the lockscreen. */
+@SysUISingleton
+class KeyguardServiceShowLockscreenRepository @Inject constructor() {
+ val showLockscreenCallbacks = ArrayList<ShowLockscreenCallback>()
+
+ /**
+ * Adds a callback that we'll notify when we show the lockscreen (or affirmatively decide not to
+ * show it).
+ */
+ fun addShowLockscreenCallback(forUser: Int, callback: IRemoteCallback) {
+ synchronized(showLockscreenCallbacks) {
+ showLockscreenCallbacks.add(ShowLockscreenCallback(forUser, callback))
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index ab0efed2cb76..02e04aa279d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -226,6 +226,10 @@ constructor(
}
fun handleFidgetTap(x: Float, y: Float) {
+ if (!com.android.systemui.Flags.clockFidgetAnimation()) {
+ return
+ }
+
if (selectedClockSize.value == ClockSizeSetting.DYNAMIC) {
clockEventController.handleFidgetTap(x, y)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
index b55bb383c308..6c084038f328 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
@@ -17,16 +17,27 @@
package com.android.systemui.keyguard.domain.interactor
import android.annotation.SuppressLint
+import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
+import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
/**
- * Logic around requests by [KeyguardService] to show keyguard right now, even though the device is
- * awake and not going to sleep.
+ * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
+ * the device is awake and not going to sleep.
*
* This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
* because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
@@ -38,7 +49,28 @@ import kotlinx.coroutines.launch
@SysUISingleton
class KeyguardServiceShowLockscreenInteractor
@Inject
-constructor(@Background val backgroundScope: CoroutineScope) {
+constructor(
+ @Background val backgroundScope: CoroutineScope,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val repository: KeyguardServiceShowLockscreenRepository,
+ private val userTracker: UserTracker,
+ private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) : CoreStartable {
+
+ override fun start() {
+ backgroundScope.launch {
+ // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
+ // This is not the only place we notify the lockNowCallbacks - there are cases where we
+ // decide not to show the lockscreen despite being asked to, and we need to notify the
+ // callback in those cases as well.
+ wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
+ if (visible) {
+ notifyShowLockscreenCallbacks()
+ }
+ }
+ }
+ }
/**
* Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
@@ -57,9 +89,38 @@ constructor(@Background val backgroundScope: CoroutineScope) {
/**
* Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
* the device locked while the screen was on.
+ *
+ * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
+ * later when we successfully show.
*/
- fun onKeyguardServiceDoKeyguardTimeout() {
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
backgroundScope.launch {
+ if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
+ val userId = userTracker.userId
+
+ // This callback needs to be invoked after we show the lockscreen (or decide not to
+ // show it) otherwise System UI will crash in 20 seconds, as a security measure.
+ repository.addShowLockscreenCallback(
+ userId,
+ IRemoteCallback.Stub.asInterface(
+ options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
+ ),
+ )
+
+ Log.d(
+ TAG,
+ "Showing lockscreen now - setting required callback for user $userId. " +
+ "SysUI will crash if this callback is not invoked.",
+ )
+
+ // If the keyguard is disabled or suppressed, we'll never actually show the
+ // lockscreen. Notify the callback so we don't crash.
+ if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
+ Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
+ notifyShowLockscreenCallbacks()
+ }
+ }
+
showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
}
}
@@ -74,4 +135,31 @@ constructor(@Background val backgroundScope: CoroutineScope) {
showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
}
}
+
+ /** Notifies the callbacks that we've either locked, or decided not to lock. */
+ private fun notifyShowLockscreenCallbacks() {
+ var callbacks: MutableList<ShowLockscreenCallback>
+
+ synchronized(repository.showLockscreenCallbacks) {
+ callbacks = ArrayList(repository.showLockscreenCallbacks)
+ repository.showLockscreenCallbacks.clear()
+ }
+
+ callbacks.forEach { callback ->
+ if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
+ Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
+ return
+ }
+ Log.i(TAG, "Notifying lockNowCallback")
+ try {
+ callback.remoteCallback.sendResult(null)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
index a8000a568a6a..c67939a0738a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
@@ -16,15 +16,20 @@
package com.android.systemui.keyguard.domain.interactor
+import android.annotation.SuppressLint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/** The reason we're showing lockscreen while awake, used for logging. */
enum class ShowWhileAwakeReason(private val logReason: String) {
@@ -38,6 +43,9 @@ enum class ShowWhileAwakeReason(private val logReason: String) {
),
KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
"Timed out while the screen was kept on, or WM#lockNow() was called."
+ ),
+ SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY(
+ "User switch to secure user occurred during keyguardGoingAway sequence, so we're locking."
);
override fun toString(): String {
@@ -68,6 +76,7 @@ enum class ShowWhileAwakeReason(private val logReason: String) {
class KeyguardShowWhileAwakeInteractor
@Inject
constructor(
+ @Background val backgroundScope: CoroutineScope,
biometricSettingsRepository: BiometricSettingsRepository,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
@@ -91,6 +100,15 @@ constructor(
.filter { reshow -> reshow }
.map { ShowWhileAwakeReason.KEYGUARD_REENABLED }
+ /**
+ * Emits whenever a user switch to a secure user occurs during keyguard going away.
+ *
+ * This is an event flow, hence the SharedFlow.
+ */
+ @SuppressLint("SharedFlowCreation")
+ val switchedToSecureUserDuringGoingAway: MutableSharedFlow<ShowWhileAwakeReason> =
+ MutableSharedFlow()
+
/** Emits whenever we should show lockscreen while the screen is on, for any reason. */
val showWhileAwakeEvents: Flow<ShowWhileAwakeReason> =
merge(
@@ -108,5 +126,15 @@ constructor(
keyguardServiceShowLockscreenInteractor.showNowEvents.filter {
keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()
},
+ switchedToSecureUserDuringGoingAway,
)
+
+ /** A user switch to a secure user occurred while we were going away. We need to re-lock. */
+ fun onSwitchedToSecureUserWhileKeyguardGoingAway() {
+ backgroundScope.launch {
+ switchedToSecureUserDuringGoingAway.emit(
+ ShowWhileAwakeReason.SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 2b4582a2a096..780b7fb06b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@ constructor(
private val auditLogger: KeyguardTransitionAuditLogger,
private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
+ private val keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
) : CoreStartable {
override fun start() {
@@ -54,6 +55,7 @@ constructor(
auditLogger.start()
statusBarDisableFlagsInteractor.start()
keyguardStateCallbackInteractor.start()
+ keyguardServiceShowLockscreenInteractor.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 6d8a943d3e28..830afeac7b96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -31,10 +31,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
@@ -165,43 +163,27 @@ constructor(
.onStart { emit(false) }
.distinctUntilChanged()
- private val isOnLockscreen: Flow<Boolean> =
+ private val isOnOrGoingToLockscreen: Flow<Boolean> =
combine(
- keyguardTransitionInteractor.isFinishedIn(LOCKSCREEN).onStart { emit(false) },
- anyOf(
- keyguardTransitionInteractor.isInTransition(Edge.create(to = LOCKSCREEN)),
- keyguardTransitionInteractor.isInTransition(Edge.create(from = LOCKSCREEN)),
- ),
- ) { onLockscreen, transitioningToOrFromLockscreen ->
- onLockscreen || transitioningToOrFromLockscreen
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it == 1f },
+ keyguardTransitionInteractor.isInTransition(Edge.create(to = LOCKSCREEN)),
+ ) { onLockscreen, transitioningToLockscreen ->
+ onLockscreen || transitioningToLockscreen
}
.distinctUntilChanged()
private val alphaOnShadeExpansion: Flow<Float> =
combineTransform(
- anyOf(
- keyguardTransitionInteractor.isInTransition(
- edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
- edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
- ),
- keyguardTransitionInteractor.isInTransition(
- edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN),
- edgeWithoutSceneContainer =
- Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
- ),
- keyguardTransitionInteractor.isInTransition(
- Edge.create(from = LOCKSCREEN, to = DREAMING)
- ),
- keyguardTransitionInteractor.isInTransition(
- Edge.create(from = LOCKSCREEN, to = OCCLUDED)
- ),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN),
+ edgeWithoutSceneContainer = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
),
- isOnLockscreen,
+ isOnOrGoingToLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.shadeExpansion,
- ) { disabledTransitionRunning, isOnLockscreen, qsExpansion, shadeExpansion ->
+ ) { disabledTransitionRunning, isOnOrGoingToLockscreen, qsExpansion, shadeExpansion ->
// Fade out quickly as the shade expands
- if (isOnLockscreen && !disabledTransitionRunning) {
+ if (isOnOrGoingToLockscreen && !disabledTransitionRunning) {
val alpha =
1f -
MathUtils.constrainedMap(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 3758afa61ed4..9312bca04994 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -66,6 +66,9 @@ constructor(
transitionAnimation.sharedFlow(
duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
onStep = alphaForAnimationStep,
+ // Rapid swipes to bouncer, and may end up skipping intermediate values that would've
+ // caused a complete fade out of lockscreen elements. Ensure it goes to 0f.
+ onFinish = { 0f },
)
val lockscreenAlpha: Flow<Float> = shortcutsAlpha
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 309b6751176c..c2efc7559487 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -1256,7 +1256,7 @@ class LegacyMediaDataManagerImpl(
return MediaAction(
Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
- context.getString(R.string.controls_media_resume),
+ context.getString(R.string.controls_media_button_play),
if (Flags.mediaControlsUiUpdate()) {
context.getDrawable(R.drawable.ic_media_play_button_container)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index dbd2250a75b0..a7c5a36b804a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -521,7 +521,7 @@ constructor(
return MediaAction(
Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
- context.getString(R.string.controls_media_resume),
+ context.getString(R.string.controls_media_button_play),
if (Flags.mediaControlsUiUpdate()) {
context.getDrawable(R.drawable.ic_media_play_button_container)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 94df4b353c94..ca4a65953cba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -1193,7 +1193,7 @@ class MediaDataProcessor(
return MediaAction(
Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
action,
- context.getString(R.string.controls_media_resume),
+ context.getString(R.string.controls_media_button_play),
if (Flags.mediaControlsUiUpdate()) {
context.getDrawable(R.drawable.ic_media_play_button_container)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index cedf661d8ee3..5b65531cdd55 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,7 +127,6 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
holder.seekBar.setMax(data.duration)
- val totalTimeDescription = data.durationDescription
if (data.scrubbing) {
holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
@@ -147,17 +146,9 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
}
- val elapsedTimeDescription = data.elapsedTimeDescription
if (data.scrubbing) {
holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
-
- holder.seekBar.contentDescription =
- holder.seekBar.context.getString(
- R.string.controls_media_seekbar_description,
- elapsedTimeDescription,
- totalTimeDescription,
- )
}
}
@@ -166,6 +157,18 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
}
+ fun updateContentDescription(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ holder.seekBar.contentDescription =
+ holder.seekBar.context.getString(
+ R.string.controls_media_seekbar_description,
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index ac6343c6bb64..71b3223b77be 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -159,8 +159,8 @@ constructor(
/** Is the player currently visible (at the end of the transformation */
private var playersVisible: Boolean = false
- /** Are we currently disabling pagination only allowing one media session to show */
- private var currentlyDisablePagination: Boolean = false
+ /** Are we currently disabling scolling, only allowing the first media session to show */
+ private var currentlyDisableScrolling: Boolean = false
/**
* The desired location where we'll be at the end of the transformation. Usually this matches
@@ -1130,21 +1130,22 @@ constructor(
val endShowsActive = hostStates[currentEndLocation]?.showsOnlyActiveMedia ?: true
val startShowsActive =
hostStates[currentStartLocation]?.showsOnlyActiveMedia ?: endShowsActive
- val startDisablePagination = hostStates[currentStartLocation]?.disablePagination ?: false
- val endDisablePagination = hostStates[currentEndLocation]?.disablePagination ?: false
+ val startDisableScrolling = hostStates[currentStartLocation]?.disableScrolling ?: false
+ val endDisableScrolling = hostStates[currentEndLocation]?.disableScrolling ?: false
if (
currentlyShowingOnlyActive != endShowsActive ||
- currentlyDisablePagination != endDisablePagination ||
+ currentlyDisableScrolling != endDisableScrolling ||
((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
(startShowsActive != endShowsActive ||
- startDisablePagination != endDisablePagination))
+ startDisableScrolling != endDisableScrolling))
) {
// Whenever we're transitioning from between differing states or the endstate differs
// we reset the translation
currentlyShowingOnlyActive = endShowsActive
- currentlyDisablePagination = endDisablePagination
+ currentlyDisableScrolling = endDisableScrolling
mediaCarouselScrollHandler.resetTranslation(animate = true)
+ mediaCarouselScrollHandler.scrollingDisabled = currentlyDisableScrolling
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 006eb203a669..f69985ee5364 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -224,6 +224,8 @@ public class MediaControlPanel {
this::setIsScrubbing;
private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
this::setIsSeekBarEnabled;
+ private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
+ this::setSeekbarContentDescription;
private final BroadcastDialogController mBroadcastDialogController;
private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@ public class MediaControlPanel {
}
mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
mSeekBarViewModel.onDestroy();
mMediaViewController.onDestroy();
}
@@ -395,6 +398,10 @@ public class MediaControlPanel {
});
}
+ private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
+ mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+ }
+
/**
* Reloads animator duration scale.
*/
@@ -424,6 +431,7 @@ public class MediaControlPanel {
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
mMediaViewController.attach(player);
vh.getPlayer().setOnLongClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index dba190022c8b..e87d5de56177 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -229,6 +229,20 @@ constructor(
}
}
+ private val seekbarDescriptionListener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ if (!SceneContainerFlag.isEnabled) return
+ seekBarObserver.updateContentDescription(
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+ }
+
/**
* Sets the listening state of the player.
*
@@ -350,6 +364,7 @@ constructor(
}
seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
seekBarViewModel.onDestroy()
}
mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@ constructor(
seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)
val mediaCard = mediaViewHolder.player
attach(mediaViewHolder.player)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 9cf4a7b3a007..68865d65139c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -127,6 +127,9 @@ class MediaCarouselScrollHandler(
scrollView.relativeScrollX = newRelativeScroll
}
+ /** Is scrolling disabled for the carousel */
+ var scrollingDisabled: Boolean = false
+
/** Does the dismiss currently show the setting cog? */
var showsSettingsButton: Boolean = false
@@ -270,6 +273,10 @@ class MediaCarouselScrollHandler(
}
private fun onTouch(motionEvent: MotionEvent): Boolean {
+ if (scrollingDisabled) {
+ return false
+ }
+
val isUp = motionEvent.action == MotionEvent.ACTION_UP
if (gestureDetector.onTouchEvent(motionEvent)) {
if (isUp) {
@@ -349,6 +356,10 @@ class MediaCarouselScrollHandler(
}
fun onScroll(down: MotionEvent, lastMotion: MotionEvent, distanceX: Float): Boolean {
+ if (scrollingDisabled) {
+ return false
+ }
+
val totalX = lastMotion.x - down.x
val currentTranslation = scrollView.getContentTranslation()
if (currentTranslation != 0.0f || !scrollView.canScrollHorizontally((-totalX).toInt())) {
@@ -405,6 +416,10 @@ class MediaCarouselScrollHandler(
}
private fun onFling(vX: Float, vY: Float): Boolean {
+ if (scrollingDisabled) {
+ return false
+ }
+
if (vX * vX < 0.5 * vY * vY) {
return false
}
@@ -575,6 +590,9 @@ class MediaCarouselScrollHandler(
* @param destIndex destination index to indicate where the scroll should end.
*/
fun scrollToPlayer(sourceIndex: Int = -1, destIndex: Int) {
+ if (scrollingDisabled) {
+ return
+ }
if (sourceIndex >= 0 && sourceIndex < mediaContent.childCount) {
scrollView.relativeScrollX = sourceIndex * playerWidthPlusPadding
}
@@ -596,6 +614,9 @@ class MediaCarouselScrollHandler(
* @param step A positive number means next, and negative means previous.
*/
fun scrollByStep(step: Int) {
+ if (scrollingDisabled) {
+ return
+ }
val destIndex = visibleMediaIndex + step
if (destIndex >= mediaContent.childCount || destIndex < 0) {
if (!showsSettingsButton) return
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index a518349ea424..37af7642b105 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -295,7 +295,7 @@ class MediaHost(
changedListener?.invoke()
}
- override var disablePagination: Boolean = false
+ override var disableScrolling: Boolean = false
set(value) {
if (field == value) {
return
@@ -320,7 +320,7 @@ class MediaHost(
mediaHostState.visible = visible
mediaHostState.disappearParameters = disappearParameters.deepCopy()
mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded
- mediaHostState.disablePagination = disablePagination
+ mediaHostState.disableScrolling = disableScrolling
return mediaHostState
}
@@ -349,7 +349,7 @@ class MediaHost(
if (!disappearParameters.equals(other.disappearParameters)) {
return false
}
- if (disablePagination != other.disablePagination) {
+ if (disableScrolling != other.disableScrolling) {
return false
}
return true
@@ -363,7 +363,7 @@ class MediaHost(
result = 31 * result + showsOnlyActiveMedia.hashCode()
result = 31 * result + if (visible) 1 else 2
result = 31 * result + disappearParameters.hashCode()
- result = 31 * result + disablePagination.hashCode()
+ result = 31 * result + disableScrolling.hashCode()
return result
}
}
@@ -423,10 +423,11 @@ interface MediaHostState {
var disappearParameters: DisappearParameters
/**
- * Whether pagination should be disabled for this host, meaning that when there are multiple
- * media sessions, only the first one will appear.
+ * Whether scrolling should be disabled for this host, meaning that when there are multiple
+ * media sessions, it will not be possible to scroll between media sessions or swipe away the
+ * entire media carousel. The first media session will always be shown.
*/
- var disablePagination: Boolean
+ var disableScrolling: Boolean
/** Get a copy of this view state, deepcopying all appropriate members */
fun copy(): MediaHostState
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 8744c5c9a838..78a8cf8e9432 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -104,19 +104,20 @@ constructor(
)
set(value) {
val enabledChanged = value.enabled != field.enabled
+ field = value
if (enabledChanged) {
enabledChangeListener?.onEnabledChanged(value.enabled)
}
+ _progress.postValue(value)
+
bgExecutor.execute {
val durationDescription = formatTimeContentDescription(value.duration)
val elapsedDescription =
value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
- field =
- value.copy(
- durationDescription = durationDescription,
- elapsedTimeDescription = elapsedDescription,
- )
- _progress.postValue(field)
+ contentDescriptionListener?.onContentDescriptionChanged(
+ elapsedDescription,
+ durationDescription,
+ )
}
}
@@ -175,6 +176,7 @@ constructor(
private var scrubbingChangeListener: ScrubbingChangeListener? = null
private var enabledChangeListener: EnabledChangeListener? = null
+ private var contentDescriptionListener: ContentDescriptionListener? = null
/** Set to true when the user is touching the seek bar to change the position. */
private var scrubbing = false
@@ -394,6 +396,16 @@ constructor(
}
}
+ fun setContentDescriptionListener(listener: ContentDescriptionListener) {
+ contentDescriptionListener = listener
+ }
+
+ fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
+ if (listener == contentDescriptionListener) {
+ contentDescriptionListener = null
+ }
+ }
+
/** returns a pair of whether seekbar is enabled and the duration of media. */
private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@ constructor(
fun onEnabledChanged(enabled: Boolean)
}
+ interface ContentDescriptionListener {
+ fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ )
+ }
+
private class SeekBarChangeListener(
val viewModel: SeekBarViewModel,
val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@ constructor(
val duration: Int,
/** whether seekBar is listening to progress updates */
val listening: Boolean,
- val elapsedTimeDescription: CharSequence = "",
- val durationDescription: CharSequence = "",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index f79693138e24..d0c6a3e6a3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -66,6 +66,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -78,7 +79,6 @@ import com.android.settingslib.media.InputMediaDevice;
import com.android.settingslib.media.InputRouteManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogTransitionAnimator;
@@ -226,7 +226,7 @@ public class MediaSwitchingController
InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm, token);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
- mOutputMediaItemListProxy = new OutputMediaItemListProxy();
+ mOutputMediaItemListProxy = new OutputMediaItemListProxy(context);
mDialogTransitionAnimator = dialogTransitionAnimator;
mNearbyMediaDevicesManager = nearbyMediaDevicesManager;
mMediaOutputColorSchemeLegacy = MediaOutputColorSchemeLegacy.fromSystemColors(mContext);
@@ -308,7 +308,8 @@ public class MediaSwitchingController
}
private MediaController getMediaController() {
- if (mToken != null && Flags.usePlaybackInfoForRoutingControls()) {
+ if (mToken != null
+ && com.android.settingslib.media.flags.Flags.usePlaybackInfoForRoutingControls()) {
return new MediaController(mContext, mToken);
} else {
for (NotificationEntry entry : mNotifCollection.getAllNotifs()) {
@@ -577,19 +578,35 @@ public class MediaSwitchingController
private void buildMediaItems(List<MediaDevice> devices) {
synchronized (mMediaDevicesLock) {
- List<MediaItem> updatedMediaItems =
- buildMediaItems(mOutputMediaItemListProxy.getOutputMediaItemList(), devices);
- mOutputMediaItemListProxy.clearAndAddAll(updatedMediaItems);
+ if (!mLocalMediaManager.isPreferenceRouteListingExist()) {
+ attachRangeInfo(devices);
+ Collections.sort(devices, Comparator.naturalOrder());
+ }
+ if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+ // For the first time building list, to make sure the top device is the connected
+ // device.
+ boolean needToHandleMutingExpectedDevice =
+ hasMutingExpectedDevice() && !isCurrentConnectedDeviceRemote();
+ final MediaDevice connectedMediaDevice =
+ needToHandleMutingExpectedDevice ? null : getCurrentConnectedMediaDevice();
+ mOutputMediaItemListProxy.updateMediaDevices(
+ devices,
+ getSelectedMediaDevice(),
+ connectedMediaDevice,
+ needToHandleMutingExpectedDevice,
+ getConnectNewDeviceItem());
+ } else {
+ List<MediaItem> updatedMediaItems =
+ buildMediaItems(
+ mOutputMediaItemListProxy.getOutputMediaItemList(), devices);
+ mOutputMediaItemListProxy.clearAndAddAll(updatedMediaItems);
+ }
}
}
protected List<MediaItem> buildMediaItems(
List<MediaItem> oldMediaItems, List<MediaDevice> devices) {
synchronized (mMediaDevicesLock) {
- if (!mLocalMediaManager.isPreferenceRouteListingExist()) {
- attachRangeInfo(devices);
- Collections.sort(devices, Comparator.naturalOrder());
- }
// For the first time building list, to make sure the top device is the connected
// device.
boolean needToHandleMutingExpectedDevice =
@@ -648,8 +665,7 @@ public class MediaSwitchingController
.map(MediaItem::createDeviceMediaItem)
.collect(Collectors.toList());
- boolean shouldAddFirstSeenSelectedDevice =
- com.android.media.flags.Flags.enableOutputSwitcherDeviceGrouping();
+ boolean shouldAddFirstSeenSelectedDevice = Flags.enableOutputSwitcherDeviceGrouping();
if (shouldAddFirstSeenSelectedDevice) {
finalMediaItems.clear();
@@ -675,7 +691,7 @@ public class MediaSwitchingController
}
private boolean enableInputRouting() {
- return com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl();
+ return Flags.enableAudioInputDeviceRoutingAndVolumeControl();
}
private void buildInputMediaItems(List<MediaDevice> devices) {
@@ -703,8 +719,7 @@ public class MediaSwitchingController
if (connectedMediaDevice != null) {
selectedDevicesIds.add(connectedMediaDevice.getId());
}
- boolean groupSelectedDevices =
- com.android.media.flags.Flags.enableOutputSwitcherDeviceGrouping();
+ boolean groupSelectedDevices = Flags.enableOutputSwitcherDeviceGrouping();
int nextSelectedItemIndex = 0;
boolean suggestedDeviceAdded = false;
boolean displayGroupAdded = false;
@@ -879,6 +894,11 @@ public class MediaSwitchingController
return mLocalMediaManager.getCurrentConnectedDevice();
}
+ @VisibleForTesting
+ void clearMediaItemList() {
+ mOutputMediaItemListProxy.clear();
+ }
+
boolean addDeviceToPlayMedia(MediaDevice device) {
mMetricLogger.logInteractionExpansion(device);
return mLocalMediaManager.addDeviceToPlayMedia(device);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java
index 1c9c0b102cb7..45ca2c6ee8e5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,22 +16,175 @@
package com.android.systemui.media.dialog;
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.media.flags.Flags;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.res.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
/** A proxy of holding the list of Output Switcher's output media items. */
public class OutputMediaItemListProxy {
+ private final Context mContext;
private final List<MediaItem> mOutputMediaItemList;
- public OutputMediaItemListProxy() {
+ // Use separated lists to hold different media items and create the list of output media items
+ // by using those separated lists and group dividers.
+ private final List<MediaItem> mSelectedMediaItems;
+ private final List<MediaItem> mSuggestedMediaItems;
+ private final List<MediaItem> mSpeakersAndDisplaysMediaItems;
+ @Nullable private MediaItem mConnectNewDeviceMediaItem;
+
+ public OutputMediaItemListProxy(Context context) {
+ mContext = context;
mOutputMediaItemList = new CopyOnWriteArrayList<>();
+ mSelectedMediaItems = new CopyOnWriteArrayList<>();
+ mSuggestedMediaItems = new CopyOnWriteArrayList<>();
+ mSpeakersAndDisplaysMediaItems = new CopyOnWriteArrayList<>();
}
/** Returns the list of output media items. */
public List<MediaItem> getOutputMediaItemList() {
+ if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+ if (isEmpty() && !mOutputMediaItemList.isEmpty()) {
+ // Ensures mOutputMediaItemList is empty when all individual media item lists are
+ // empty, preventing unexpected state issues.
+ mOutputMediaItemList.clear();
+ } else if (!isEmpty() && mOutputMediaItemList.isEmpty()) {
+ // When any individual media item list is modified, the cached mOutputMediaItemList
+ // is emptied. On the next request for the output media item list, a fresh list is
+ // created and stored in the cache.
+ mOutputMediaItemList.addAll(createOutputMediaItemList());
+ }
+ }
return mOutputMediaItemList;
}
+ private List<MediaItem> createOutputMediaItemList() {
+ List<MediaItem> finalMediaItems = new CopyOnWriteArrayList<>();
+ finalMediaItems.addAll(mSelectedMediaItems);
+ if (!mSuggestedMediaItems.isEmpty()) {
+ finalMediaItems.add(
+ MediaItem.createGroupDividerMediaItem(
+ mContext.getString(
+ R.string.media_output_group_title_suggested_device)));
+ finalMediaItems.addAll(mSuggestedMediaItems);
+ }
+ if (!mSpeakersAndDisplaysMediaItems.isEmpty()) {
+ finalMediaItems.add(
+ MediaItem.createGroupDividerMediaItem(
+ mContext.getString(
+ R.string.media_output_group_title_speakers_and_displays)));
+ finalMediaItems.addAll(mSpeakersAndDisplaysMediaItems);
+ }
+ if (mConnectNewDeviceMediaItem != null) {
+ finalMediaItems.add(mConnectNewDeviceMediaItem);
+ }
+ return finalMediaItems;
+ }
+
+ /** Updates the list of output media items with a given list of media devices. */
+ public void updateMediaDevices(
+ List<MediaDevice> devices,
+ List<MediaDevice> selectedDevices,
+ @Nullable MediaDevice connectedMediaDevice,
+ boolean needToHandleMutingExpectedDevice,
+ @Nullable MediaItem connectNewDeviceMediaItem) {
+ Set<String> selectedOrConnectedMediaDeviceIds =
+ selectedDevices.stream().map(MediaDevice::getId).collect(Collectors.toSet());
+ if (connectedMediaDevice != null) {
+ selectedOrConnectedMediaDeviceIds.add(connectedMediaDevice.getId());
+ }
+
+ List<MediaItem> selectedMediaItems = new ArrayList<>();
+ List<MediaItem> suggestedMediaItems = new ArrayList<>();
+ List<MediaItem> speakersAndDisplaysMediaItems = new ArrayList<>();
+ Map<String, MediaItem> deviceIdToMediaItemMap = new HashMap<>();
+ buildMediaItems(
+ devices,
+ selectedOrConnectedMediaDeviceIds,
+ needToHandleMutingExpectedDevice,
+ selectedMediaItems,
+ suggestedMediaItems,
+ speakersAndDisplaysMediaItems,
+ deviceIdToMediaItemMap);
+
+ List<MediaItem> updatedSelectedMediaItems = new CopyOnWriteArrayList<>();
+ List<MediaItem> updatedSuggestedMediaItems = new CopyOnWriteArrayList<>();
+ List<MediaItem> updatedSpeakersAndDisplaysMediaItems = new CopyOnWriteArrayList<>();
+ if (isEmpty()) {
+ updatedSelectedMediaItems.addAll(selectedMediaItems);
+ updatedSuggestedMediaItems.addAll(suggestedMediaItems);
+ updatedSpeakersAndDisplaysMediaItems.addAll(speakersAndDisplaysMediaItems);
+ } else {
+ Set<String> updatedDeviceIds = new HashSet<>();
+ // Preserve the existing media item order while updating with the latest device
+ // information. Some items may retain their original group (suggested, speakers and
+ // displays) to maintain this order.
+ updateMediaItems(
+ mSelectedMediaItems,
+ updatedSelectedMediaItems,
+ deviceIdToMediaItemMap,
+ updatedDeviceIds);
+ updateMediaItems(
+ mSuggestedMediaItems,
+ updatedSuggestedMediaItems,
+ deviceIdToMediaItemMap,
+ updatedDeviceIds);
+ updateMediaItems(
+ mSpeakersAndDisplaysMediaItems,
+ updatedSpeakersAndDisplaysMediaItems,
+ deviceIdToMediaItemMap,
+ updatedDeviceIds);
+
+ // Append new media items that are not already in the existing lists to the output list.
+ List<MediaItem> remainingMediaItems = new ArrayList<>();
+ remainingMediaItems.addAll(
+ getRemainingMediaItems(selectedMediaItems, updatedDeviceIds));
+ remainingMediaItems.addAll(
+ getRemainingMediaItems(suggestedMediaItems, updatedDeviceIds));
+ remainingMediaItems.addAll(
+ getRemainingMediaItems(speakersAndDisplaysMediaItems, updatedDeviceIds));
+ updatedSpeakersAndDisplaysMediaItems.addAll(remainingMediaItems);
+ }
+
+ if (Flags.enableOutputSwitcherDeviceGrouping() && !updatedSelectedMediaItems.isEmpty()) {
+ MediaItem selectedMediaItem = updatedSelectedMediaItems.get(0);
+ Optional<MediaDevice> mediaDeviceOptional = selectedMediaItem.getMediaDevice();
+ if (mediaDeviceOptional.isPresent()) {
+ MediaItem updatedMediaItem =
+ MediaItem.createDeviceMediaItem(
+ mediaDeviceOptional.get(), /* isFirstDeviceInGroup= */ true);
+ updatedSelectedMediaItems.remove(0);
+ updatedSelectedMediaItems.add(0, updatedMediaItem);
+ }
+ }
+
+ mSelectedMediaItems.clear();
+ mSelectedMediaItems.addAll(updatedSelectedMediaItems);
+ mSuggestedMediaItems.clear();
+ mSuggestedMediaItems.addAll(updatedSuggestedMediaItems);
+ mSpeakersAndDisplaysMediaItems.clear();
+ mSpeakersAndDisplaysMediaItems.addAll(updatedSpeakersAndDisplaysMediaItems);
+ mConnectNewDeviceMediaItem = connectNewDeviceMediaItem;
+
+ // The cached mOutputMediaItemList is cleared upon any update to individual media item
+ // lists. This ensures getOutputMediaItemList() computes and caches a fresh list on the next
+ // invocation.
+ mOutputMediaItemList.clear();
+ }
+
/** Updates the list of output media items with the given list. */
public void clearAndAddAll(List<MediaItem> updatedMediaItems) {
mOutputMediaItemList.clear();
@@ -40,16 +193,112 @@ public class OutputMediaItemListProxy {
/** Removes the media items with muting expected devices. */
public void removeMutingExpectedDevices() {
+ if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+ mSelectedMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+ mSuggestedMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+ mSpeakersAndDisplaysMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+ if (mConnectNewDeviceMediaItem != null
+ && mConnectNewDeviceMediaItem.isMutingExpectedDevice()) {
+ mConnectNewDeviceMediaItem = null;
+ }
+ }
mOutputMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
}
/** Clears the output media item list. */
public void clear() {
+ if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+ mSelectedMediaItems.clear();
+ mSuggestedMediaItems.clear();
+ mSpeakersAndDisplaysMediaItems.clear();
+ mConnectNewDeviceMediaItem = null;
+ }
mOutputMediaItemList.clear();
}
/** Returns whether the output media item list is empty. */
public boolean isEmpty() {
- return mOutputMediaItemList.isEmpty();
+ if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+ return mSelectedMediaItems.isEmpty()
+ && mSuggestedMediaItems.isEmpty()
+ && mSpeakersAndDisplaysMediaItems.isEmpty()
+ && (mConnectNewDeviceMediaItem == null);
+ } else {
+ return mOutputMediaItemList.isEmpty();
+ }
+ }
+
+ private void buildMediaItems(
+ List<MediaDevice> devices,
+ Set<String> selectedOrConnectedMediaDeviceIds,
+ boolean needToHandleMutingExpectedDevice,
+ List<MediaItem> selectedMediaItems,
+ List<MediaItem> suggestedMediaItems,
+ List<MediaItem> speakersAndDisplaysMediaItems,
+ Map<String, MediaItem> deviceIdToMediaItemMap) {
+ for (MediaDevice device : devices) {
+ String deviceId = device.getId();
+ MediaItem mediaItem = MediaItem.createDeviceMediaItem(device);
+ if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
+ selectedMediaItems.add(0, mediaItem);
+ } else if (!needToHandleMutingExpectedDevice
+ && selectedOrConnectedMediaDeviceIds.contains(device.getId())) {
+ if (Flags.enableOutputSwitcherDeviceGrouping()) {
+ selectedMediaItems.add(mediaItem);
+ } else {
+ selectedMediaItems.add(0, mediaItem);
+ }
+ } else if (device.isSuggestedDevice()) {
+ suggestedMediaItems.add(mediaItem);
+ } else {
+ speakersAndDisplaysMediaItems.add(mediaItem);
+ }
+ deviceIdToMediaItemMap.put(deviceId, mediaItem);
+ }
+ }
+
+ /** Returns a list of media items that remains the same order as the existing media items. */
+ private void updateMediaItems(
+ List<MediaItem> existingMediaItems,
+ List<MediaItem> updatedMediaItems,
+ Map<String, MediaItem> deviceIdToMediaItemMap,
+ Set<String> updatedDeviceIds) {
+ List<String> existingDeviceIds = getDeviceIds(existingMediaItems);
+ for (String deviceId : existingDeviceIds) {
+ MediaItem mediaItem = deviceIdToMediaItemMap.get(deviceId);
+ if (mediaItem != null) {
+ updatedMediaItems.add(mediaItem);
+ updatedDeviceIds.add(deviceId);
+ }
+ }
+ }
+
+ /**
+ * Returns media items from the input list that are not associated with the given device IDs.
+ */
+ private List<MediaItem> getRemainingMediaItems(
+ List<MediaItem> mediaItems, Set<String> deviceIds) {
+ List<MediaItem> remainingMediaItems = new ArrayList<>();
+ for (MediaItem item : mediaItems) {
+ Optional<MediaDevice> mediaDeviceOptional = item.getMediaDevice();
+ if (mediaDeviceOptional.isPresent()) {
+ String deviceId = mediaDeviceOptional.get().getId();
+ if (!deviceIds.contains(deviceId)) {
+ remainingMediaItems.add(item);
+ }
+ }
+ }
+ return remainingMediaItems;
+ }
+
+ /** Returns a list of media device IDs for the given list of media items. */
+ private List<String> getDeviceIds(List<MediaItem> mediaItems) {
+ List<String> deviceIds = new ArrayList<>();
+ for (MediaItem item : mediaItems) {
+ if (item != null && item.getMediaDevice().isPresent()) {
+ deviceIds.add(item.getMediaDevice().get().getId());
+ }
+ }
+ return deviceIds;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index 8751aa31f237..f07238895aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -73,6 +73,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -94,9 +95,12 @@ import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -106,6 +110,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastRoundToInt
import com.android.compose.PlatformButton
import com.android.compose.PlatformIconButton
import com.android.compose.PlatformOutlinedButton
@@ -134,6 +139,7 @@ import com.android.systemui.media.remedia.ui.viewmodel.MediaPlayPauseActionViewM
import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
import kotlin.math.max
+import kotlin.math.min
/**
* Renders a media controls UI element.
@@ -414,12 +420,56 @@ private fun ContentScope.CardForegroundContent(
.clip(CircleShape),
)
+ var cardMaxWidth: Int by remember { mutableIntStateOf(0) }
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
- modifier = Modifier.align(Alignment.TopEnd),
+ modifier =
+ Modifier.align(Alignment.TopEnd)
+ // Output switcher chips must each be limited to at most 40% of the maximum
+ // width of the card.
+ //
+ // This saves the maximum possible width of the card so it can be referred
+ // to by child custom layout code below.
+ //
+ // The assumption is that the row can be as wide as the entire card.
+ .layout { measurable, constraints ->
+ cardMaxWidth = constraints.maxWidth
+ val placeable = measurable.measure(constraints)
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
) {
viewModel.outputSwitcherChips.fastForEach { chip ->
- OutputSwitcherChip(viewModel = chip, colorScheme = colorScheme)
+ OutputSwitcherChip(
+ viewModel = chip,
+ colorScheme = colorScheme,
+ modifier =
+ Modifier
+ // Each chip must be limited to 40% of the width of the card at
+ // most.
+ //
+ // The underlying assumption is that there'll never be more than one
+ // chip with text and one more icon-only chip. Only the one with
+ // text can ever end up being too wide.
+ .layout { measurable, constraints ->
+ val placeable =
+ measurable.measure(
+ constraints.copy(
+ maxWidth =
+ min(
+ (cardMaxWidth * 0.4f).fastRoundToInt(),
+ constraints.maxWidth,
+ )
+ )
+ )
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
+ )
}
}
}
@@ -663,11 +713,20 @@ private fun ContentScope.Navigation(
if (isSeekBarVisible) {
// To allow the seek bar slider to fade in and out, it's tagged as an element.
Element(key = Media.Elements.SeekBarSlider, modifier = Modifier.weight(1f)) {
+ val sliderDragDelta = remember {
+ // Not a mutableStateOf - this is never accessed in composition and
+ // using an anonymous object avoids generics boxing of inline Offset.
+ object {
+ var value = Offset.Zero
+ }
+ }
Slider(
interactionSource = interactionSource,
value = viewModel.progress,
onValueChange = { progress -> viewModel.onScrubChange(progress) },
- onValueChangeFinished = { viewModel.onScrubFinished() },
+ onValueChangeFinished = {
+ viewModel.onScrubFinished(sliderDragDelta.value)
+ },
colors = colors,
thumb = {
SeekBarThumb(interactionSource = interactionSource, colors = colors)
@@ -681,9 +740,43 @@ private fun ContentScope.Navigation(
)
},
modifier =
- Modifier.fillMaxWidth().clearAndSetSemantics {
- contentDescription = viewModel.contentDescription
- },
+ Modifier.fillMaxWidth()
+ .clearAndSetSemantics {
+ contentDescription = viewModel.contentDescription
+ }
+ .pointerInput(Unit) {
+ // Track and report the drag delta to the view-model so it
+ // can
+ // decide whether to accept the next onValueChangeFinished
+ // or
+ // reject it if the drag was overly vertical.
+ awaitPointerEventScope {
+ var down: PointerInputChange? = null
+ while (true) {
+ val event =
+ awaitPointerEvent(PointerEventPass.Initial)
+ when (event.type) {
+ PointerEventType.Press -> {
+ // A new gesture has begun. Record the
+ // initial
+ // down input change.
+ down = event.changes.last()
+ }
+
+ PointerEventType.Move -> {
+ // The pointer has moved. If it's the same
+ // pointer as the latest down, calculate and
+ // report the drag delta.
+ val change = event.changes.last()
+ if (change.id == down?.id) {
+ sliderDragDelta.value =
+ change.position - down.position
+ }
+ }
+ }
+ }
+ }
+ },
)
}
}
@@ -979,6 +1072,8 @@ private fun OutputSwitcherChip(
text = viewModel.text,
style = MaterialTheme.typography.bodySmall,
color = colorScheme.onPrimary,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
index ca7334341c87..a3689926e937 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.remedia.ui.viewmodel
import androidx.annotation.FloatRange
+import androidx.compose.ui.geometry.Offset
/**
* Models UI state for the navigation component of the UI (potentially containing the seek bar and
@@ -58,7 +59,7 @@ sealed interface MediaNavigationViewModel {
* A callback to invoke once the user finishes "scrubbing" (e.g. stopped moving the thumb of
* the seek bar). The position/progress should be committed.
*/
- val onScrubFinished: () -> Unit,
+ val onScrubFinished: (delta: Offset) -> Unit,
/** Accessibility string to attach to the seekbar UI element. */
val contentDescription: String,
) : MediaNavigationViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
index 15951165a19e..19b08fa212db 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
@@ -26,9 +26,9 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ImageBitmap
import com.android.systemui.classifier.Classifier
-import com.android.systemui.classifier.domain.interactor.runIfNotFalseTap
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -42,6 +42,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.Locale
+import kotlin.math.abs
import kotlin.math.roundToLong
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.awaitCancellation
@@ -114,8 +115,11 @@ constructor(
isScrubbing = true
seekProgress = progress
},
- onScrubFinished = {
- if (!falsingSystem.isFalseTouch(Classifier.MEDIA_SEEKBAR)) {
+ onScrubFinished = { dragDelta ->
+ if (
+ dragDelta.isHorizontal() &&
+ !falsingSystem.isFalseTouch(Classifier.MEDIA_SEEKBAR)
+ ) {
interactor.seek(
sessionKey = session.key,
to = (seekProgress * session.durationMs).roundToLong(),
@@ -346,6 +350,14 @@ constructor(
.formatMeasures(*measures.toTypedArray())
}
+ /**
+ * Returns `true` if this [Offset] is the same or larger on the horizontal axis than the
+ * vertical axis.
+ */
+ private fun Offset.isHorizontal(): Boolean {
+ return abs(x) >= abs(y)
+ }
+
interface FalsingSystem {
fun runIfNotFalseTap(@FalsingManager.Penalty penalty: Int, block: () -> Unit)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 49fa3ba2da61..88f679e73388 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -72,4 +72,8 @@ public interface NavigationBarController {
/** @return {@link NavigationBar} on the default display. */
@Nullable
NavigationBar getDefaultNavigationBar();
+
+ /** @return {@link NavigationBar} for a specific display, or null if not available. */
+ @Nullable
+ NavigationBar getNavigationBar(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
index 45ff7f4f87ef..f096510fa5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -54,4 +54,6 @@ class NavigationBarControllerEmptyImpl @Inject constructor() : NavigationBarCont
override fun isOverviewEnabled(displayId: Int) = false
override fun getDefaultNavigationBar(): NavigationBar? = null
+
+ override fun getNavigationBar(displayId: Int): NavigationBar? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 50d0a459da66..8fbf8b60af9a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -469,7 +469,8 @@ public class NavigationBarControllerImpl implements
return (navBar == null) ? null : navBar.getView();
}
- private @Nullable NavigationBar getNavigationBar(int displayId) {
+ @Override
+ public @Nullable NavigationBar getNavigationBar(int displayId) {
return mNavigationBars.get(displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 914e0f74e4a0..58ddbf60e8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -27,10 +27,13 @@ import android.view.WindowManager;
import com.android.app.viewcapture.ViewCapture;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.views.NavigationBarFrame;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import dagger.Lazy;
import dagger.Module;
@@ -71,6 +74,20 @@ public interface NavigationBarModule {
return context.getSystemService(WindowManager.class);
}
+ /** A SysUiState for the navigation bar display. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static SysUiState provideSysUiState(@DisplayId Context context,
+ SysUiState defaultState,
+ PerDisplayRepository<SysUiState> repository) {
+ if (ShadeWindowGoesAround.isEnabled()) {
+ return repository.get(context.getDisplayId());
+ } else {
+ return defaultState;
+ }
+ }
+
/** A ViewCaptureAwareWindowManager specific to the display's context. */
@Provides
@NavigationBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index f95f45906b23..8b5b3adeef1f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -569,7 +569,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
NavigationModeController navigationModeController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- SysUiState sysUiFlagsContainer,
+ @DisplayId SysUiState sysUiFlagsContainer,
UserTracker userTracker,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
@@ -1694,7 +1694,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
(mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
- .commitUpdate(mDisplayId);
+ .commitUpdate();
}
private void updateAssistantEntrypoints(boolean assistantAvailable,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index 36cb8fa374b0..cbc4c26b2f94 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -740,15 +740,13 @@ public class NavigationBarView extends FrameLayout {
/** */
public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
- int displayId = mContext.getDisplayId();
-
sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
.setFlag(SYSUI_STATE_SEARCH_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
- .commitUpdate(displayId);
+ .commitUpdate();
}
public void setInScreenPinning(boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index a4386dee7264..05a60a6db31e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs.composefragment
import android.annotation.SuppressLint
import android.content.Context
-import android.content.res.Configuration
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
@@ -49,7 +48,6 @@ import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -72,8 +70,6 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -255,7 +251,7 @@ constructor(
@Composable
private fun Content() {
- PlatformTheme(isDarkTheme = true /* Delete AlwaysDarkMode when removing this */) {
+ PlatformTheme {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
// TODO(b/389985793): Make sure that there is no coroutine work or recompositions
// happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -747,25 +743,22 @@ constructor(
)
val BrightnessSlider =
@Composable {
- AlwaysDarkMode {
- Box(
- Modifier.systemGestureExclusionInShade(
- enabled = {
- layoutState.transitionState is TransitionState.Idle
- }
- )
- ) {
- BrightnessSliderContainer(
- viewModel =
- containerViewModel.brightnessSliderViewModel,
- containerColors =
- ContainerColors(
- Color.Transparent,
- ContainerColors.defaultContainerColor,
- ),
- modifier = Modifier.fillMaxWidth(),
- )
- }
+ Box(
+ Modifier.systemGestureExclusionInShade(
+ enabled = {
+ layoutState.transitionState is TransitionState.Idle
+ }
+ )
+ ) {
+ BrightnessSliderContainer(
+ viewModel = containerViewModel.brightnessSliderViewModel,
+ containerColors =
+ ContainerColors(
+ Color.Transparent,
+ ContainerColors.defaultContainerColor,
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ )
}
}
val TileGrid =
@@ -1243,28 +1236,3 @@ private fun interactionsConfig() =
private inline val alwaysCompose
get() = Flags.alwaysComposeQsUiFragment()
-
-/**
- * Forces the configuration and themes to be dark theme. This is needed in order to have
- * [colorResource] retrieve the dark mode colors.
- *
- * This should be removed when we remove the force dark mode in [PlatformTheme] at the root of the
- * compose hierarchy.
- */
-@Composable
-private fun AlwaysDarkMode(content: @Composable () -> Unit) {
- val currentConfig = LocalConfiguration.current
- val darkConfig =
- Configuration(currentConfig).apply {
- uiMode =
- (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or
- Configuration.UI_MODE_NIGHT_YES
- }
- val newContext = LocalContext.current.createConfigurationContext(darkConfig)
- CompositionLocalProvider(
- LocalConfiguration provides darkConfig,
- LocalContext provides newContext,
- ) {
- content()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 22971a9eb703..a7ebb2289814 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -88,7 +88,11 @@ constructor(
LaunchedEffect(listening, pagerState) {
snapshotFlow { listening() }
.collect {
- if (!listening()) {
+ // Whenever we go from not listening to listening, we should be in the first
+ // page. If we did this when going from listening to not listening, opening
+ // edit mode in second page will cause it to go to first page during the
+ // transition.
+ if (listening()) {
pagerState.scrollToPage(0)
}
}
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 69b967a68c3c..ccbd8fdbe00c 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
@@ -63,6 +63,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -89,6 +90,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.Color
@@ -113,7 +115,9 @@ 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 com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
+import com.android.compose.theme.LocalAndroidColorScheme
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
@@ -131,6 +135,7 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaul
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.GridBackgroundCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.ResizingState
@@ -163,14 +168,27 @@ object TileType
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
-
+ val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
- title = { Text(text = stringResource(id = R.string.qs_edit)) },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = Color.Transparent,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ title = {
+ Text(
+ text = stringResource(id = R.string.qs_edit),
+ modifier = Modifier.padding(start = 24.dp),
+ )
+ },
navigationIcon = {
- IconButton(onClick = onStopEditing) {
+ IconButton(
+ onClick = onStopEditing,
+ modifier = Modifier.drawBehind { drawCircle(primaryContainerColor) },
+ ) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
+ tint = MaterialTheme.colorScheme.onSurface,
contentDescription =
stringResource(id = com.android.internal.R.string.action_bar_up_description),
)
@@ -178,11 +196,19 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
},
actions = {
if (onReset != null) {
- TextButton(onClick = onReset) {
+ TextButton(
+ onClick = onReset,
+ colors =
+ ButtonDefaults.textButtonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ ) {
Text(stringResource(id = com.android.internal.R.string.reset))
}
}
},
+ modifier = Modifier.padding(vertical = 8.dp),
)
}
@@ -215,7 +241,9 @@ fun DefaultEditTileGrid(
containerColor = Color.Transparent,
topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
) { innerPadding ->
- CompositionLocalProvider(LocalOverscrollFactory provides null) {
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
val scrollState = rememberScrollState()
AutoScrollGrid(listState, scrollState, innerPadding)
@@ -244,7 +272,7 @@ fun DefaultEditTileGrid(
targetState = listState.dragInProgress || selectionState.selected,
label = "QSEditHeader",
contentAlignment = Alignment.Center,
- modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
+ modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
) { showRemoveTarget ->
EditGridHeader {
if (showRemoveTarget) {
@@ -289,10 +317,6 @@ fun DefaultEditTileGrid(
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
modifier = modifier.fillMaxSize(),
) {
- EditGridHeader {
- Text(text = stringResource(id = R.string.drag_to_add_tiles))
- }
-
val availableTiles = remember {
mutableStateListOf<AvailableTileGridCell>().apply {
addAll(toAvailableTiles(listState.tiles, otherTiles))
@@ -371,9 +395,7 @@ private fun EditGridHeader(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit,
) {
- CompositionLocalProvider(
- LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
- ) {
+ CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { content() }
}
}
@@ -420,6 +442,7 @@ private fun CurrentTilesGrid(
listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
}
+ val primaryColor = MaterialTheme.colorScheme.primary
TileLazyGrid(
state = gridState,
columns = GridCells.Fixed(columns),
@@ -428,9 +451,9 @@ private fun CurrentTilesGrid(
Modifier.fillMaxWidth()
.height { totalHeight.roundToPx() }
.border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
- shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
+ width = 2.dp,
+ color = primaryColor,
+ shape = RoundedCornerShape(GridBackgroundCornerRadius),
)
.dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
onSetTiles(currentListState.tileSpecs())
@@ -439,6 +462,13 @@ private fun CurrentTilesGrid(
.onGloballyPositioned { coordinates ->
gridContentOffset = coordinates.positionInRoot()
}
+ .drawBehind {
+ drawRoundRect(
+ primaryColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .15f,
+ )
+ }
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
@@ -469,7 +499,6 @@ private fun AvailableTileGrid(
remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
groupAndSort(tiles)
}
- val labelColors = EditModeTileDefaults.editTileColors()
// Available tiles
Column(
@@ -480,32 +509,45 @@ private fun AvailableTileGrid(
) {
groupedTiles.forEach { (category, tiles) ->
key(category) {
- Text(
- text = category.label.load() ?: "",
- fontSize = 20.sp,
- color = labelColors.label,
+ val surfaceColor = MaterialTheme.colorScheme.surface
+ Column(
+ verticalArrangement = spacedBy(16.dp),
modifier =
- Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
- )
- tiles.chunked(columns).forEach { row ->
- Row(
- horizontalArrangement = spacedBy(TileArrangementPadding),
- modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
- ) {
- row.forEach { tileGridCell ->
- key(tileGridCell.key) {
- AvailableTileGridCell(
- cell = tileGridCell,
- dragAndDropState = dragAndDropState,
- selectionState = selectionState,
- onAddTile = onAddTile,
- modifier = Modifier.weight(1f).fillMaxHeight(),
+ Modifier.drawBehind {
+ drawRoundRect(
+ surfaceColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .32f,
)
}
- }
+ .padding(16.dp),
+ ) {
+ Text(
+ text = category.label.load() ?: "",
+ fontSize = 20.sp,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
+ )
+ tiles.chunked(columns).forEach { row ->
+ Row(
+ horizontalArrangement = spacedBy(TileArrangementPadding),
+ modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
+ ) {
+ row.forEach { tileGridCell ->
+ key(tileGridCell.key) {
+ AvailableTileGridCell(
+ cell = tileGridCell,
+ dragAndDropState = dragAndDropState,
+ selectionState = selectionState,
+ onAddTile = onAddTile,
+ modifier = Modifier.weight(1f).fillMaxHeight(),
+ )
+ }
+ }
- // Spacers for incomplete rows
- repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ // Spacers for incomplete rows
+ repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ }
}
}
}
@@ -761,7 +803,7 @@ private fun AvailableTileGridCell(
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
- modifier = Modifier.align(Alignment.Center),
+ modifier = Modifier.align(Alignment.TopCenter),
)
}
}
@@ -861,15 +903,16 @@ private object EditModeTileDefaults {
const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
val CurrentTilesGridPadding = 10.dp
val AvailableTilesGridMinHeight = 200.dp
+ val GridBackgroundCornerRadius = 42.dp
@Composable
fun editTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
}
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 6236ada46374..27e609232a4c 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
@@ -78,8 +78,15 @@ constructor(
}
val columns = columnsWithMediaViewModel.columns
+ val largeTiles by iconTilesViewModel.largeTilesState
val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
- val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width(largeTilesSpan)) }
+ // Tiles or largeTiles may be updated while this is composed, so listen to any changes
+ val sizedTiles =
+ remember(tiles, largeTiles, largeTilesSpan) {
+ tiles.map {
+ SizedTileImpl(it, if (largeTiles.contains(it.spec)) largeTilesSpan else 1)
+ }
+ }
val bounceables =
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
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 bf63c3858542..6bafd432669a 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
@@ -25,6 +25,7 @@ import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@@ -59,6 +60,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.semantics.Role
@@ -74,7 +76,9 @@ import androidx.compose.ui.util.trace
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.Expandable
import com.android.compose.animation.bounceable
+import com.android.compose.animation.rememberExpandableController
import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -165,6 +169,7 @@ fun Tile(
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+ val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
TileExpandable(
color = { animatedColor },
@@ -181,7 +186,8 @@ fun Tile(
nextBounceable = currentBounceableInfo.nextTile,
orientation = Orientation.Horizontal,
bounceEnd = currentBounceableInfo.bounceEnd,
- ),
+ )
+ .graphicsLayer { alpha = animatedAlpha },
) { expandable ->
val longClick: (() -> Unit)? =
{
@@ -260,8 +266,7 @@ private fun TileExpandable(
content: @Composable (Expandable) -> Unit,
) {
Expandable(
- color = color(),
- shape = shape,
+ controller = rememberExpandableController(color = color, shape = shape),
modifier = modifier.clip(shape).verticalSquish(squishiness),
useModifierBasedImplementation = true,
) {
@@ -370,6 +375,7 @@ data class TileColors(
val label: Color,
val secondaryLabel: Color,
val icon: Color,
+ val alpha: Float = 1f,
)
private object TileDefaults {
@@ -393,10 +399,10 @@ private object TileDefaults {
@ReadOnlyComposable
fun activeDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
iconBackground = MaterialTheme.colorScheme.primary,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onPrimary,
)
@@ -404,34 +410,36 @@ private object TileDefaults {
@ReadOnlyComposable
fun inactiveDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect3,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
@Composable
@ReadOnlyComposable
fun inactiveTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
@Composable
@ReadOnlyComposable
- fun unavailableTileColors(): TileColors =
- TileColors(
- background = MaterialTheme.colorScheme.surface,
- iconBackground = MaterialTheme.colorScheme.surface,
+ fun unavailableTileColors(): TileColors {
+ return TileColors(
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
+ alpha = .38f,
)
+ }
@Composable
@ReadOnlyComposable
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 a66b51f6fe50..57f63c755b43 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
@@ -62,7 +62,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.size
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.BADGE_ANGLE_RAD
@@ -155,6 +154,7 @@ fun InteractiveTileContainer(
Icon(
Icons.Default.Remove,
contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier =
Modifier.size(size).align(Alignment.Center).graphicsLayer {
this.alpha = badgeIconAlpha
@@ -218,14 +218,15 @@ fun StaticTileBadge(
)
}
) {
- val secondaryColor = MaterialTheme.colorScheme.secondary
val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
+ val primaryColor = MaterialTheme.colorScheme.primary
Icon(
icon,
contentDescription = contentDescription,
+ tint = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.size(size).align(Alignment.Center).drawBehind {
- drawCircle(secondaryColor, radius = BadgeSize.toPx() / 2)
+ drawCircle(primaryColor, radius = BadgeSize.toPx() / 2)
},
)
}
@@ -291,7 +292,7 @@ private fun Transition<TileState>.animateColor(): State<Color> {
return animateColor { state ->
when (state) {
None -> Color.Transparent
- Removable -> MaterialTheme.colorScheme.secondary
+ Removable -> MaterialTheme.colorScheme.primaryContainer
Selected -> MaterialTheme.colorScheme.primary
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
index a9d673aa7400..d6705a68d32c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
@@ -35,6 +35,9 @@ constructor(
private val hydrator = Hydrator("DynamicIconTilesViewModel")
private val interactor = interactorFactory.create()
+ val largeTilesState =
+ hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
+
val largeTilesSpanState =
hydrator.hydratedStateOf(
traceName = "largeTilesSpan",
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index bc15bbb5e57d..263ef09ea767 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -20,6 +20,8 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.feature.flags.Flags
import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -28,8 +30,11 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
/**
* Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
@@ -46,6 +51,7 @@ internal constructor(
private val rearDisplayStateInteractor: RearDisplayStateInteractor,
private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
@Application private val scope: CoroutineScope,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : CoreStartable, AutoCloseable {
companion object {
@@ -53,6 +59,16 @@ internal constructor(
}
@VisibleForTesting var stateChangeListener: Job? = null
+ private val keyguardVisible = MutableStateFlow(false)
+ private val keyguardVisibleFlow = keyguardVisible.asStateFlow()
+
+ @VisibleForTesting
+ val keyguardCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onKeyguardVisibilityChanged(visible: Boolean) {
+ keyguardVisible.value = visible
+ }
+ }
override fun close() {
stateChangeListener?.cancel()
@@ -62,28 +78,39 @@ internal constructor(
if (Flags.deviceStateRdmV2()) {
var dialog: SystemUIDialog? = null
+ keyguardUpdateMonitor.registerCallback(keyguardCallback)
+
stateChangeListener =
- rearDisplayStateInteractor.state
- .map {
- when (it) {
- is RearDisplayStateInteractor.State.Enabled -> {
- val rearDisplayContext =
- context.createDisplayContext(it.innerDisplay)
- val delegate =
- rearDisplayInnerDialogDelegateFactory.create(
- rearDisplayContext,
- deviceStateManager::cancelStateRequest,
- )
- dialog = delegate.createDialog().apply { show() }
- }
+ scope.launch {
+ combine(rearDisplayStateInteractor.state, keyguardVisibleFlow) {
+ rearDisplayState,
+ keyguardVisible ->
+ Pair(rearDisplayState, keyguardVisible)
+ }
+ .collectLatest { (rearDisplayState, keyguardVisible) ->
+ when (rearDisplayState) {
+ is RearDisplayStateInteractor.State.Enabled -> {
+ if (!keyguardVisible) {
+ val rearDisplayContext =
+ context.createDisplayContext(
+ rearDisplayState.innerDisplay
+ )
+ val delegate =
+ rearDisplayInnerDialogDelegateFactory.create(
+ rearDisplayContext,
+ deviceStateManager::cancelStateRequest,
+ )
+ dialog = delegate.createDialog().apply { show() }
+ }
+ }
- is RearDisplayStateInteractor.State.Disabled -> {
- dialog?.dismiss()
- dialog = null
+ is RearDisplayStateInteractor.State.Disabled -> {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
- }
- .launchIn(scope)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 4be35f147c2f..d2639654d206 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -89,6 +89,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.display.data.repository.DisplayRepository;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -109,6 +111,7 @@ import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.recents.ILauncherProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -156,7 +159,9 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
private final Executor mMainExecutor;
private final ShellInterface mShellInterface;
private final Lazy<ShadeViewController> mShadeViewControllerLazy;
- private SysUiState mSysUiState;
+ private final PerDisplayRepository<SysUiState> mPerDisplaySysUiStateRepository;
+ private final DisplayRepository mDisplayRepository;
+ private SysUiState mDefaultDisplaySysUIState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
private final ScreenPinningRequest mScreenPinningRequest;
@@ -586,9 +591,12 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
// Force-update the systemui state flags
updateSystemUiStateFlags();
- // TODO b/398011576 - send the state for all displays.
- notifySystemUiStateFlags(mSysUiState.getFlags(), Display.DEFAULT_DISPLAY);
-
+ if (ShadeWindowGoesAround.isEnabled()) {
+ notifySysUiStateFlagsForAllDisplays();
+ } else {
+ notifySystemUiStateFlags(mDefaultDisplaySysUIState.getFlags(),
+ Display.DEFAULT_DISPLAY);
+ }
notifyConnectionChanged();
}
@@ -614,6 +622,18 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
}
};
+ /** Propagates the flags for all displays to be notified to Launcher. */
+ @VisibleForTesting
+ public void notifySysUiStateFlagsForAllDisplays() {
+ var displays = mDisplayRepository.getDisplayIds().getValue();
+ for (int displayId : displays) {
+ var state = mPerDisplaySysUiStateRepository.get(displayId);
+ if (state != null) {
+ notifySystemUiStateFlags(state.getFlags(), displayId);
+ }
+ }
+ }
+
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
// This is the death handler for the binder from the launcher service
@@ -671,7 +691,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
ScreenPinningRequest screenPinningRequest,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController,
- SysUiState sysUiState,
+ PerDisplayRepository<SysUiState> perDisplaySysUiStateRepository,
Provider<SceneInteractor> sceneInteractor,
Provider<ShadeInteractor> shadeInteractor,
UserTracker userTracker,
@@ -686,7 +706,8 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
BroadcastDispatcher broadcastDispatcher,
Optional<BackAnimation> backAnimation,
- ProcessWrapper processWrapper
+ ProcessWrapper processWrapper,
+ DisplayRepository displayRepository
) {
// b/241601880: This component should only be running for primary users or
// secondaryUsers when visibleBackgroundUsers are supported.
@@ -718,10 +739,10 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
com.android.internal.R.string.config_recentsComponentName));
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
- // TODO b/398011576 - Here we're still only handling the default display state. We should
- // have a callback for any sysuiState change.
- mSysUiState = sysUiState;
- mSysUiState.addCallback(mSysUiStateCallback);
+ mPerDisplaySysUiStateRepository = perDisplaySysUiStateRepository;
+ mDisplayRepository = displayRepository;
+ mDefaultDisplaySysUIState = perDisplaySysUiStateRepository.get(Display.DEFAULT_DISPLAY);
+ mDefaultDisplaySysUIState.addCallback(mSysUiStateCallback);
mUiEventLogger = uiEventLogger;
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
@@ -770,7 +791,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
if (mLauncherProxy != null) {
try {
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && (sysUiState.getFlags()
+ && (mDefaultDisplaySysUIState.getFlags()
& SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
return;
}
@@ -795,7 +816,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
}
public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
- mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
+ mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
.commitUpdate(mContext.getDisplayId());
}
@@ -804,23 +825,42 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
startConnectionToCurrentUser();
}
- private void updateSystemUiStateFlags() {
+ private void updateSysUIStateForNavbars() {
+ if (ShadeWindowGoesAround.isEnabled()) {
+ var displays = mDisplayRepository.getDisplayIds().getValue();
+ for (int displayId : displays) {
+ updateSysUIStateForNavbarWithDisplayId(displayId);
+ }
+ } else {
+ updateSysUIStateForNavbarWithDisplayId(Display.DEFAULT_DISPLAY);
+ }
+ }
+
+ private void updateSysUIStateForNavbarWithDisplayId(int displayId) {
final NavigationBar navBarFragment =
- mNavBarControllerLazy.get().getDefaultNavigationBar();
+ mNavBarControllerLazy.get().getNavigationBar(displayId);
final NavigationBarView navBarView =
- mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
+ mNavBarControllerLazy.get().getNavigationBarView(displayId);
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView
+ " shadeViewController=" + mShadeViewControllerLazy.get());
}
+ final SysUiState displaySysuiState = mPerDisplaySysUiStateRepository.get(displayId);
+ if (displaySysuiState == null) return;
+
if (navBarFragment != null) {
navBarFragment.updateSystemUiStateFlags();
}
if (navBarView != null) {
- navBarView.updateDisabledSystemUiStateFlags(mSysUiState);
+ navBarView.updateDisabledSystemUiStateFlags(displaySysuiState);
}
+ }
+
+ /** Force updates SystemUI state flags prior to sending them to Launcher. */
+ public void updateSystemUiStateFlags() {
+ updateSysUIStateForNavbars();
mShadeViewControllerLazy.get().updateSystemUiStateFlags();
if (mStatusBarWinController != null) {
mStatusBarWinController.notifyStateChangedCallbacks();
@@ -845,7 +885,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing,
boolean panelExpanded, boolean isDreaming, boolean communalShowing) {
- mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+ mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
@@ -1122,7 +1162,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedWakingUp() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, true)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
.commitUpdate(mContext.getDisplayId());
@@ -1130,7 +1170,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
@Override
public void onFinishedWakingUp() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, true)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
.commitUpdate(mContext.getDisplayId());
@@ -1138,7 +1178,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
@Override
public void onStartedGoingToSleep() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, false)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
.commitUpdate(mContext.getDisplayId());
@@ -1146,7 +1186,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
@Override
public void onFinishedGoingToSleep() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, false)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
.commitUpdate(mContext.getDisplayId());
@@ -1247,7 +1287,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
pw.print(" mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
- mSysUiState.dump(pw, args);
+ mDefaultDisplaySysUIState.dump(pw, args);
}
public interface LauncherProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f45971b57b65..2bacee12db8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -86,7 +86,7 @@ open class BlurUtils @Inject constructor(
*/
fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
- !supportsBlursOnWindows() || earlyWakeupEnabled
+ !shouldBlur(radius) || earlyWakeupEnabled
) {
return
}
@@ -113,7 +113,7 @@ open class BlurUtils @Inject constructor(
return
}
createTransaction().use {
- if (supportsBlursOnWindows()) {
+ if (shouldBlur(radius)) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
Trace.asyncTraceForTrackBegin(
@@ -142,6 +142,14 @@ open class BlurUtils @Inject constructor(
return SurfaceControl.Transaction()
}
+ private fun shouldBlur(radius: Int): Boolean {
+ return supportsBlursOnWindows() ||
+ ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) &&
+ supportsBlursOnWindowsBase() &&
+ lastAppliedBlur > 0 &&
+ radius == 0)
+ }
+
/**
* If this device can render blurs.
*
@@ -149,8 +157,11 @@ open class BlurUtils @Inject constructor(
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
+ return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+ }
+
+ private fun supportsBlursOnWindowsBase(): Boolean {
return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
- crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
!SystemProperties.getBoolean("persist.sysui.disableBlur", false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 05ef1645c1d6..d2f424a46620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -394,7 +394,7 @@ constructor(
// Only drag down on sensitive views, otherwise the ExpandHelper will take this
return if (NotificationBundleUi.isEnabled)
view.entryAdapter?.isSensitive?.value == true
- else view.entry.isSensitive.value
+ else view.entryLegacy.isSensitive.value
}
}
return false
@@ -569,7 +569,7 @@ constructor(
if (NotificationBundleUi.isEnabled) {
userId = expandView.entryAdapter?.sbn?.userId!!
} else {
- userId = expandView.entry.sbn.userId
+ userId = expandView.entryLegacy.sbn.userId
}
}
var fullShadeNeedsBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 339f898be251..9bf3d5dfe4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -47,7 +47,6 @@ import android.database.ExecutorContentObserver;
import android.net.Uri;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -78,6 +77,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -920,7 +920,9 @@ public class NotificationLockscreenUserManagerImpl implements
// notification's "when" time, or the notification entry creation time
private long getEarliestNotificationTime(NotificationEntry notif) {
long notifWhenWallClock = notif.getSbn().getNotification().getWhen();
- long creationTimeDelta = SystemClock.uptimeMillis() - notif.getCreationTime();
+ long creationTimeDelta = UseElapsedRealtimeForCreationTime.getCurrentTime()
+ - notif.getCreationTime();
+
long creationTimeWallClock = System.currentTimeMillis() - creationTimeDelta;
return Math.min(notifWhenWallClock, creationTimeWallClock);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 041ed6504634..485d5b2ab555 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@ public class NotificationRemoteInputManager implements CoreStartable {
if (NotificationBundleUi.isEnabled()) {
releaseNotificationIfKeptForRemoteInputHistory(row.getEntryAdapter());
} else {
- releaseNotificationIfKeptForRemoteInputHistory(row.getEntry());
+ releaseNotificationIfKeptForRemoteInputHistory(row.getEntryLegacy());
}
}
return started;
@@ -189,8 +189,8 @@ public class NotificationRemoteInputManager implements CoreStartable {
statusBarNotification = row.getEntryAdapter().getSbn();
}
} else {
- if (row.getEntry() != null) {
- statusBarNotification = row.getEntry().getSbn();
+ if (row.getEntryLegacy() != null) {
+ statusBarNotification = row.getEntryLegacy().getSbn();
}
}
if (statusBarNotification == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 2e83910a2a93..472dc823423e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Configuration
import android.os.SystemClock
import android.util.IndentingPrintWriter
import android.util.Log
@@ -42,16 +40,14 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
@@ -82,13 +78,11 @@ constructor(
private val wallpaperInteractor: WallpaperInteractor,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val dozeParameters: DozeParameters,
- @ShadeDisplayAware private val context: Context,
- private val splitShadeStateController: SplitShadeStateController,
+ private val shadeModeInteractor: ShadeModeInteractor,
private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
private val appZoomOutOptional: Optional<AppZoomOut>,
@Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
- configurationController: ConfigurationController,
) : ShadeExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -110,7 +104,6 @@ constructor(
private var isOpen: Boolean = false
private var isBlurred: Boolean = false
private var listeners = mutableListOf<DepthListener>()
- private var inSplitShade: Boolean = false
private var prevTracking: Boolean = false
private var prevTimestamp: Long = -1
@@ -294,7 +287,7 @@ constructor(
private fun blurRadiusToZoomOut(blurRadius: Float): Float {
var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
- if (inSplitShade) {
+ if (shadeModeInteractor.isSplitShade) {
zoomOut = 0f
}
@@ -432,14 +425,6 @@ constructor(
}
shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- updateResources()
- configurationController.addCallback(
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- updateResources()
- }
- }
- )
applicationScope.launch {
wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
wallpaperSupportsAmbientMode = supported
@@ -469,10 +454,6 @@ constructor(
}
}
- private fun updateResources() {
- inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
- }
-
fun addListener(listener: DepthListener) {
listeners.add(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f88c618a9acc..c2a87cddee55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -675,7 +675,7 @@ public class NotificationShelf extends ActivatableNotificationView {
}
StatusBarIconView icon = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getIcons().getShelfIcon()
- : row.getEntry().getIcons().getShelfIcon();
+ : row.getEntryLegacy().getIcons().getShelfIcon();
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
int top = (int) (maxTop - shelfIconPosition);
@@ -689,7 +689,7 @@ public class NotificationShelf extends ActivatableNotificationView {
private void updateContinuousClipping(final ExpandableNotificationRow row) {
StatusBarIconView icon = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getIcons().getShelfIcon()
- : row.getEntry().getIcons().getShelfIcon();
+ : row.getEntryLegacy().getIcons().getShelfIcon();
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 6cebcd98a0ba..6d3c12d139db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -18,10 +18,10 @@ per-file *Keyguard* = file:../keyguard/OWNERS
per-file *Notification* = file:notification/OWNERS
# Files that control blur effects on shade
per-file *NotificationShadeDepth* = set noparent
-per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
per-file *Blur* = set noparent
-per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
# Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
per-file *Mode* = file:notification/OWNERS
per-file *SmartReply* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 7e7031200988..03108deb0ecc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -17,10 +17,13 @@
package com.android.systemui.statusbar.chips.call.ui.viewmodel
import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Context
import android.view.View
import com.android.internal.jank.Cuj
import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
+import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
@@ -48,7 +51,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
/** View model for the ongoing phone call chip shown in the status bar. */
@@ -63,14 +69,62 @@ constructor(
private val activityStarter: ActivityStarter,
@StatusBarChipsLog private val logger: LogBuffer,
) : OngoingActivityChipViewModel {
+ /** The transition cookie used to register and unregister launch and return animations. */
+ private val cookie =
+ ActivityTransitionAnimator.TransitionCookie("${CallChipViewModel::class.java}")
+
+ /**
+ * Used internally to determine when a launch or return animation is in progress, as these
+ * require special handling.
+ */
+ private val transitionState: MutableStateFlow<TransitionState> =
+ MutableStateFlow(TransitionState.NoTransition)
+
+ // Since we're combining the chip state and the transition state flows, getting the old value by
+ // using [pairwise()] would confuse things. This is because if the calculation is triggered by
+ // a change in transition state, the chip state will still show the previous and current values,
+ // making it difficult to figure out what actually changed. Instead we cache the old value here,
+ // so that at each update we can keep track of what actually changed.
+ private var latestState: OngoingCallModel = OngoingCallModel.NoCall
+ private var latestTransitionState: TransitionState = TransitionState.NoTransition
+
private val chipWithReturnAnimation: StateFlow<OngoingActivityChipModel> =
if (StatusBarChipsReturnAnimations.isEnabled) {
- interactor.ongoingCallState
- .map { state ->
- when (state) {
- is OngoingCallModel.NoCall -> OngoingActivityChipModel.Inactive()
+ combine(interactor.ongoingCallState, transitionState) { newState, newTransitionState ->
+ val oldState = latestState
+ latestState = newState
+ val oldTransitionState = latestTransitionState
+ latestTransitionState = newTransitionState
+
+ logger.log(
+ TAG,
+ LogLevel.DEBUG,
+ {},
+ {
+ "Call chip state updated: oldState=$oldState newState=$newState " +
+ "oldTransitionState=$oldTransitionState " +
+ "newTransitionState=$newTransitionState"
+ },
+ )
+
+ when (newState) {
+ is OngoingCallModel.NoCall ->
+ OngoingActivityChipModel.Inactive(
+ transitionManager = getTransitionManager(newState)
+ )
+
is OngoingCallModel.InCall ->
- prepareChip(state, systemClock, isHidden = state.isAppVisible)
+ prepareChip(
+ newState,
+ systemClock,
+ isHidden =
+ shouldChipBeHidden(
+ oldState = oldState,
+ newState = newState,
+ oldTransitionState = oldTransitionState,
+ newTransitionState = newTransitionState,
+ ),
+ )
}
}
.stateIn(
@@ -112,6 +166,12 @@ constructor(
chipLegacy
}
+ /**
+ * The controller factory that the call chip uses to register and unregister its transition
+ * animations.
+ */
+ private var transitionControllerFactory: ComposableControllerFactory? = null
+
/** Builds an [OngoingActivityChipModel.Active] from all the relevant information. */
private fun prepareChip(
state: OngoingCallModel.InCall,
@@ -149,6 +209,7 @@ constructor(
onClickListenerLegacy = getOnClickListener(state.intent),
clickBehavior = getClickBehavior(state.intent),
isHidden = isHidden,
+ transitionManager = getTransitionManager(state),
)
} else {
val startTimeInElapsedRealtime =
@@ -161,6 +222,7 @@ constructor(
onClickListenerLegacy = getOnClickListener(state.intent),
clickBehavior = getClickBehavior(state.intent),
isHidden = isHidden,
+ transitionManager = getTransitionManager(state),
)
}
}
@@ -191,9 +253,21 @@ constructor(
onClick = { expandable ->
StatusBarChipsModernization.unsafeAssertInNewMode()
val animationController =
- expandable.activityTransitionController(
- Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
- )
+ if (
+ !StatusBarChipsReturnAnimations.isEnabled ||
+ transitionControllerFactory == null
+ ) {
+ expandable.activityTransitionController(
+ Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
+ )
+ } else {
+ // When return animations are enabled, we use a long-lived registration
+ // with controllers created on-demand by the animation library instead
+ // of explicitly creating one at the time of the click. By not passing
+ // a controller here, we let the framework do its work. Otherwise, the
+ // explicit controller would take precedence and override the other one.
+ null
+ }
activityStarter.postStartActivityDismissingKeyguard(intent, animationController)
}
)
@@ -210,6 +284,120 @@ constructor(
)
}
+ private fun getTransitionManager(
+ state: OngoingCallModel
+ ): OngoingActivityChipModel.TransitionManager? {
+ if (!StatusBarChipsReturnAnimations.isEnabled) return null
+ return if (state is OngoingCallModel.NoCall) {
+ OngoingActivityChipModel.TransitionManager(
+ unregisterTransition = { activityStarter.unregisterTransition(cookie) }
+ )
+ } else {
+ val component = (state as OngoingCallModel.InCall).intent?.intent?.component
+ if (component != null) {
+ val factory = getTransitionControllerFactory(component)
+ OngoingActivityChipModel.TransitionManager(
+ factory,
+ registerTransition = {
+ activityStarter.registerTransition(cookie, factory, scope)
+ },
+ )
+ } else {
+ // Without a component we can't instantiate a controller factory, and without a
+ // factory registering an animation is impossible. In this case, the transition
+ // manager is empty and inert.
+ OngoingActivityChipModel.TransitionManager()
+ }
+ }
+ }
+
+ private fun getTransitionControllerFactory(
+ component: ComponentName
+ ): ComposableControllerFactory {
+ var factory = transitionControllerFactory
+ if (factory?.component == component) return factory
+
+ factory =
+ object :
+ ComposableControllerFactory(
+ cookie,
+ component,
+ launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ ) {
+ override suspend fun createController(
+ forLaunch: Boolean
+ ): ActivityTransitionAnimator.Controller {
+ transitionState.value =
+ if (forLaunch) {
+ TransitionState.LaunchRequested
+ } else {
+ TransitionState.ReturnRequested
+ }
+
+ val controller =
+ expandable
+ .mapNotNull {
+ it?.activityTransitionController(
+ launchCujType,
+ cookie,
+ component,
+ returnCujType,
+ isEphemeral = false,
+ )
+ }
+ .first()
+
+ return object : DelegateTransitionAnimatorController(controller) {
+ override val isLaunching: Boolean
+ get() = forLaunch
+
+ override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
+ delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+ transitionState.value =
+ if (isLaunching) {
+ TransitionState.Launching
+ } else {
+ TransitionState.Returning
+ }
+ }
+
+ override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
+ transitionState.value = TransitionState.NoTransition
+ }
+
+ override fun onTransitionAnimationCancelled(
+ newKeyguardOccludedState: Boolean?
+ ) {
+ delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+ transitionState.value = TransitionState.NoTransition
+ }
+ }
+ }
+ }
+
+ transitionControllerFactory = factory
+ return factory
+ }
+
+ /** Define the current state of this chip's transition animation. */
+ private sealed interface TransitionState {
+ /** Idle. */
+ data object NoTransition : TransitionState
+
+ /** Launch animation has been requested but hasn't started yet. */
+ data object LaunchRequested : TransitionState
+
+ /** Launch animation in progress. */
+ data object Launching : TransitionState
+
+ /** Return animation has been requested but hasn't started yet. */
+ data object ReturnRequested : TransitionState
+
+ /** Return animation in progress. */
+ data object Returning : TransitionState
+ }
+
companion object {
private val phoneIcon =
Icon.Resource(
@@ -217,5 +405,42 @@ constructor(
ContentDescription.Resource(R.string.ongoing_call_content_description),
)
private val TAG = "CallVM".pad()
+
+ /** Determines whether or not an active call chip should be hidden. */
+ private fun shouldChipBeHidden(
+ oldState: OngoingCallModel,
+ newState: OngoingCallModel.InCall,
+ oldTransitionState: TransitionState,
+ newTransitionState: TransitionState,
+ ): Boolean {
+ // The app is in the background and no transitions are ongoing (during transitions,
+ // [isAppVisible] must always be true). Show the chip.
+ if (!newState.isAppVisible) return false
+
+ // The call has just started and is visible. Hide the chip.
+ if (oldState is OngoingCallModel.NoCall) return true
+
+ // The state went from the app not being visible to visible. This happens when the chip
+ // is tapped and a launch animation is about to start. Keep the chip showing.
+ if (!(oldState as OngoingCallModel.InCall).isAppVisible) return false
+
+ // The app was and remains visible, but the transition state has changed. A launch or
+ // return animation has been requested or is ongoing. Keep the chip showing.
+ if (
+ newTransitionState is TransitionState.LaunchRequested ||
+ newTransitionState is TransitionState.Launching ||
+ newTransitionState is TransitionState.ReturnRequested ||
+ newTransitionState is TransitionState.Returning
+ ) {
+ return false
+ }
+
+ // The app was and remains visible, so we generally want to hide the chip. The only
+ // exception is if a return transition has just ended. In this case, the transition
+ // state changes shortly before the app visibility does. If we hide the chip between
+ // these two updates, this results in a flicker. We bridge the gap by keeping the chip
+ // showing.
+ return oldTransitionState != TransitionState.Returning
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 2fe627020ebf..1a802d634894 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -141,7 +141,7 @@ constructor(
// When we're promoting notifications automatically, the `when` time set on the
// notification will likely just be set to the current time, which would cause the chip
// to always show "now". We don't want early testers to get that experience since it's
- // not what will happen at launch, so just don't show any time.
+ // not what will happen at launch, so just don't show any time.onometerstate
return OngoingActivityChipModel.Active.IconOnly(
this.key,
icon,
@@ -194,14 +194,14 @@ constructor(
}
}
is PromotedNotificationContentModel.When.Chronometer -> {
- // TODO(b/364653005): Check isCountDown and support CountDown.
return OngoingActivityChipModel.Active.Timer(
this.key,
icon,
colors,
startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
- onClickListenerLegacy,
- clickBehavior,
+ isEventInFuture = this.promotedContent.time.isCountDown,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
index 2032ec8af78c..1eb46d8cc3d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.ui.binder
+import android.annotation.ElapsedRealtimeLong
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
object ChipChronometerBinder {
@@ -25,9 +26,11 @@ object ChipChronometerBinder {
* @param startTimeMs the time this event started, relative to
* [com.android.systemui.util.time.SystemClock.elapsedRealtime]. See
* [android.widget.Chronometer.setBase].
+ * @param isCountDown see [android.widget.Chronometer.setCountDown].
*/
- fun bind(startTimeMs: Long, view: ChipChronometer) {
+ fun bind(@ElapsedRealtimeLong startTimeMs: Long, isCountDown: Boolean, view: ChipChronometer) {
view.base = startTimeMs
+ view.isCountDown = isCountDown
view.start()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 6f8552738d33..77e0dde3dec5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -315,7 +315,11 @@ object OngoingActivityChipBinder {
chipShortTimeDeltaView.visibility = View.GONE
}
is OngoingActivityChipModel.Active.Timer -> {
- ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+ ChipChronometerBinder.bind(
+ chipModel.startTimeMs,
+ chipModel.isEventInFuture,
+ chipTimeView,
+ )
chipTimeView.visibility = View.VISIBLE
chipTextView.visibility = View.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index 55d753662a65..fa8d25623d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -74,25 +74,31 @@ fun ChipContent(viewModel: OngoingActivityChipModel.Active, modifier: Modifier =
val textMeasurer = rememberTextMeasurer()
when (viewModel) {
is OngoingActivityChipModel.Active.Timer -> {
- val timerState = rememberChronometerState(startTimeMillis = viewModel.startTimeMs)
- val text = timerState.currentTimeText
- Text(
- text = text,
- style = textStyle,
- color = textColor,
- softWrap = false,
- modifier =
- modifier
- .hideTextIfDoesNotFit(
- text = text,
- textStyle = textStyle,
- textMeasurer = textMeasurer,
- maxTextWidth = maxTextWidth,
- startPadding = startPadding,
- endPadding = endPadding,
- )
- .neverDecreaseWidth(density),
- )
+ val timerState =
+ rememberChronometerState(
+ eventTimeMillis = viewModel.startTimeMs,
+ isCountDown = viewModel.isEventInFuture,
+ timeSource = viewModel.timeSource,
+ )
+ timerState.currentTimeText?.let { text ->
+ Text(
+ text = text,
+ style = textStyle,
+ color = textColor,
+ softWrap = false,
+ modifier =
+ modifier
+ .hideTextIfDoesNotFit(
+ text = text,
+ textStyle = textStyle,
+ textMeasurer = textMeasurer,
+ maxTextWidth = maxTextWidth,
+ startPadding = startPadding,
+ endPadding = endPadding,
+ )
+ .neverDecreaseWidth(density),
+ )
+ }
}
is OngoingActivityChipModel.Active.Countdown -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 4edb23dc9f0e..58d38903f7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -42,6 +42,7 @@ import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -90,6 +91,8 @@ fun OngoingActivityChip(
},
borderStroke = borderStroke,
onClick = onClick,
+ useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+ transitionControllerFactory = model.transitionManager?.controllerFactory,
) {
ChipBody(model, iconViewStore, isClickable = onClick != null)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 7080c3402b08..700e6d93c628 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -21,12 +21,14 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -36,18 +38,30 @@ fun OngoingActivityChips(
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
modifier: Modifier = Modifier,
) {
- Row(
- modifier =
- modifier
- .fillMaxHeight()
- .padding(start = dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement =
- Arrangement.spacedBy(dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
- ) {
- chips.active
- .filter { !it.isHidden }
- .forEach {
+ if (StatusBarChipsReturnAnimations.isEnabled) {
+ SideEffect {
+ // Active chips must always be capable of animating to/from activities, even when they
+ // are hidden. Therefore we always register their transitions.
+ for (chip in chips.active) chip.transitionManager?.registerTransition?.invoke()
+ // Inactive chips and chips in the overflow are never shown, so they must not have any
+ // registered transition.
+ for (chip in chips.overflow) chip.transitionManager?.unregisterTransition?.invoke()
+ for (chip in chips.inactive) chip.transitionManager?.unregisterTransition?.invoke()
+ }
+ }
+
+ val shownChips = chips.active.filter { !it.isHidden }
+ if (shownChips.isNotEmpty()) {
+ Row(
+ modifier =
+ modifier
+ .fillMaxHeight()
+ .padding(start = dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement =
+ Arrangement.spacedBy(dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
+ ) {
+ shownChips.forEach {
key(it.key) {
OngoingActivityChip(
model = it,
@@ -56,5 +70,6 @@ fun OngoingActivityChips(
)
}
}
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 8e470742f174..3876d9fa77a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -18,12 +18,15 @@ package com.android.systemui.statusbar.chips.ui.model
import android.annotation.CurrentTimeMillisLong
import android.annotation.ElapsedRealtimeLong
+import android.os.SystemClock
import android.view.View
+import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.viewmodel.TimeSource
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
/** Model representing the display of an ongoing activity as a chip in the status bar. */
@@ -31,6 +34,9 @@ sealed class OngoingActivityChipModel {
/** Condensed name representing the model, used for logs. */
abstract val logName: String
+ /** Object used to manage the behavior of this chip during activity launch and returns. */
+ abstract val transitionManager: TransitionManager?
+
/**
* This chip shouldn't be shown.
*
@@ -38,7 +44,10 @@ sealed class OngoingActivityChipModel {
* animated, and false if that transition should *not* be animated (i.e. the chip view should
* immediately disappear).
*/
- data class Inactive(val shouldAnimate: Boolean = true) : OngoingActivityChipModel() {
+ data class Inactive(
+ val shouldAnimate: Boolean = true,
+ override val transitionManager: TransitionManager? = null,
+ ) : OngoingActivityChipModel() {
override val logName = "Inactive(anim=$shouldAnimate)"
}
@@ -59,6 +68,7 @@ sealed class OngoingActivityChipModel {
open val onClickListenerLegacy: View.OnClickListener?,
/** Data class that determines how clicks on the chip should be handled. */
open val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager?,
/**
* Whether this chip should be hidden. This can be the case depending on system states (like
* which apps are in the foreground and whether there is an ongoing transition.
@@ -75,6 +85,7 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -84,6 +95,7 @@ sealed class OngoingActivityChipModel {
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -105,8 +117,22 @@ sealed class OngoingActivityChipModel {
* [android.widget.Chronometer.setBase].
*/
@ElapsedRealtimeLong val startTimeMs: Long,
+
+ /**
+ * The [TimeSource] that should be used to track the current time for this timer. Should
+ * be compatible with [startTimeMs].
+ */
+ val timeSource: TimeSource = TimeSource { SystemClock.elapsedRealtime() },
+
+ /**
+ * True if this chip represents an event starting in the future and false if this chip
+ * represents an event that has already started. If true, [startTimeMs] should be in the
+ * future. Otherwise, [startTimeMs] should be in the past.
+ */
+ val isEventInFuture: Boolean = false,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -116,6 +142,7 @@ sealed class OngoingActivityChipModel {
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -142,6 +169,7 @@ sealed class OngoingActivityChipModel {
@CurrentTimeMillisLong val time: Long,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -151,6 +179,7 @@ sealed class OngoingActivityChipModel {
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -170,6 +199,7 @@ sealed class OngoingActivityChipModel {
override val colors: ColorsModel,
/** The number of seconds until an event is started. */
val secondsUntilStarted: Long,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -179,6 +209,7 @@ sealed class OngoingActivityChipModel {
colors,
onClickListenerLegacy = null,
clickBehavior = ClickBehavior.None,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -194,6 +225,7 @@ sealed class OngoingActivityChipModel {
val text: String,
override val onClickListenerLegacy: View.OnClickListener? = null,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -203,6 +235,7 @@ sealed class OngoingActivityChipModel {
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -256,4 +289,17 @@ sealed class OngoingActivityChipModel {
/** Clicking the chip will show the heads up notification associated with the chip. */
data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
}
+
+ /** Defines the behavior of the chip with respect to activity launch and return transitions. */
+ data class TransitionManager(
+ /** The factory used to create the controllers that animate the chip. */
+ val controllerFactory: ComposableControllerFactory? = null,
+ /**
+ * Used to create a registration for this chip using [controllerFactory]. Must be
+ * idempotent.
+ */
+ val registerTransition: () -> Unit = {},
+ /** Used to remove the existing registration for this chip, if any. */
+ val unregisterTransition: () -> Unit = {},
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
index 62789782d0a9..0fc7f82f785a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar.chips.ui.viewmodel
-import android.os.SystemClock
+import android.annotation.ElapsedRealtimeLong
import android.text.format.DateUtils.formatElapsedTime
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -27,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
+import kotlin.math.absoluteValue
import kotlinx.coroutines.delay
/** Platform-optimized interface for getting current time */
@@ -34,18 +35,50 @@ fun interface TimeSource {
fun getCurrentTime(): Long
}
-/** Holds and manages the state for a Chronometer */
-class ChronometerState(private val timeSource: TimeSource, private val startTimeMillis: Long) {
- private var currentTimeMillis by mutableLongStateOf(0L)
+/**
+ * Holds and manages the state for a Chronometer, which shows a timer in a format like "MM:SS" or
+ * "H:MM:SS".
+ *
+ * If [isEventInFuture] is false, then this Chronometer is counting up from an event that started in
+ * the past, like a phone call that was answered. [eventTimeMillis] represents the time the event
+ * started and the timer will tick up: 04:00, 04:01, ... No timer is shown if [eventTimeMillis] is
+ * in the future and [isEventInFuture] is false.
+ *
+ * If [isEventInFuture] is true, then this Chronometer is counting down to an event that will occur
+ * in the future, like a future meeting. [eventTimeMillis] represents the time the event will occur
+ * and the timer will tick down: 04:00, 03:59, ... No timer is shown if [eventTimeMillis] is in the
+ * past and [isEventInFuture] is true.
+ */
+class ChronometerState(
+ private val timeSource: TimeSource,
+ @ElapsedRealtimeLong private val eventTimeMillis: Long,
+ private val isEventInFuture: Boolean,
+) {
+ private var currentTimeMillis by mutableLongStateOf(timeSource.getCurrentTime())
private val elapsedTimeMillis: Long
- get() = maxOf(0L, currentTimeMillis - startTimeMillis)
+ get() =
+ if (isEventInFuture) {
+ eventTimeMillis - currentTimeMillis
+ } else {
+ currentTimeMillis - eventTimeMillis
+ }
- val currentTimeText: String by derivedStateOf { formatElapsedTime(elapsedTimeMillis / 1000) }
+ /**
+ * The current timer string in a format like "MM:SS" or "H:MM:SS", or null if we shouldn't show
+ * the timer string.
+ */
+ val currentTimeText: String? by derivedStateOf {
+ if (elapsedTimeMillis < 0) {
+ null
+ } else {
+ formatElapsedTime(elapsedTimeMillis / 1000)
+ }
+ }
suspend fun run() {
while (true) {
currentTimeMillis = timeSource.getCurrentTime()
- val delaySkewMillis = (currentTimeMillis - startTimeMillis) % 1000L
+ val delaySkewMillis = (eventTimeMillis - currentTimeMillis).absoluteValue % 1000L
delay(1000L - delaySkewMillis)
}
}
@@ -54,13 +87,16 @@ class ChronometerState(private val timeSource: TimeSource, private val startTime
/** Remember and manage the ChronometerState */
@Composable
fun rememberChronometerState(
- startTimeMillis: Long,
- timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+ eventTimeMillis: Long,
+ isCountDown: Boolean,
+ timeSource: TimeSource,
): ChronometerState {
val state =
- remember(timeSource, startTimeMillis) { ChronometerState(timeSource, startTimeMillis) }
+ remember(timeSource, eventTimeMillis, isCountDown) {
+ ChronometerState(timeSource, eventTimeMillis, isCountDown)
+ }
val lifecycleOwner = LocalLifecycleOwner.current
- LaunchedEffect(lifecycleOwner, timeSource, startTimeMillis) {
+ LaunchedEffect(lifecycleOwner, timeSource, eventTimeMillis) {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { state.run() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8be9e410f8f6..fcdcc3f698de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -105,7 +105,7 @@ public final class NotificationClicker implements View.OnClickListener {
mBubblesOptional.get().collapseStack();
}
} else {
- if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+ if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
}
@@ -130,7 +130,7 @@ public final class NotificationClicker implements View.OnClickListener {
} else {
row.setBubbleClickListener(v ->
mNotificationActivityStarter.onNotificationBubbleIconClicked(
- row.getEntry()));
+ row.getEntryLegacy()));
}
row.setOnClickListener(this);
row.setOnDragSuccessListener(mOnDragSuccessListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 874a059d2323..8163128f762a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -74,7 +74,6 @@ class NotificationTransitionAnimatorController(
const val ANIMATION_DURATION_TOP_ROUNDING = 100L
}
- private val notificationEntry = notification.entry
private val notificationKey = notification.key
override val isLaunching: Boolean = true
@@ -160,7 +159,7 @@ class NotificationTransitionAnimatorController(
private val headsUpNotificationRow: ExpandableNotificationRow?
get() {
val pipelineParent = if (NotificationBundleUi.isEnabled)
- notification.entryAdapter?.parent else notificationEntry.parent
+ notification.entryAdapter?.parent else notification.entryLegacy.parent
val summaryEntry = (pipelineParent as? GroupEntry)?.summary
return when {
headsUpManager.isHeadsUpEntry(notificationKey) -> notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 26c302bf6409..b1a26af336d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -113,6 +113,10 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
return false
}
+ override fun isPromotedOngoing(): Boolean {
+ return false
+ }
+
override fun isFullScreenCapable(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 3118ce56ac69..4299825bd5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -132,6 +132,11 @@ public interface EntryAdapter {
boolean isAmbient();
+ /**
+ * Returns whether this row represents promoted ongoing notification.
+ */
+ boolean isPromotedOngoing();
+
default boolean isFullScreenCapable() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index caa7abb1aa7a..8f7f61f6be65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -34,7 +34,7 @@ public abstract class ListEntry extends PipelineEntry {
}
/**
- * The SystemClock.uptimeMillis() when this object was created. In general, this means the
+ * The SystemClock.elapsedRealtime() when this object was created. In general, this means the
* moment when NotificationManager notifies our listener about the existence of this entry.
*
* This value will not change if the notification is updated, although it will change if the
@@ -65,13 +65,4 @@ public abstract class ListEntry extends PipelineEntry {
@Nullable public PipelineEntry getPreviousParent() {
return mPreviousAttachState.getParent();
}
-
- /**
- * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
- * fresh attach state (all entries will be null/default-initialized).
- */
- void beginNewAttachState() {
- mPreviousAttachState.clone(mAttachState);
- mAttachState.reset();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9795edf3313c..b7fe39e9c757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -522,7 +522,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
}
private void onNotificationsInitialized() {
- mInitializedTimestamp = mClock.uptimeMillis();
+ mInitializedTimestamp = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
}
private void postNotification(
@@ -532,7 +532,8 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ entry = new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
mEventQueue.add(new InitEntryEvent(entry));
mEventQueue.add(new BindEntryEvent(entry, sbn));
mNotificationSet.put(sbn.getKey(), entry);
@@ -861,7 +862,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
// messages from system server.
private void crashIfNotInitializing(RuntimeException exception) {
final boolean isRecentlyInitialized = mInitializedTimestamp == 0
- || mClock.uptimeMillis() - mInitializedTimestamp
+ || UseElapsedRealtimeForCreationTime.getCurrentTime(mClock) - mInitializedTimestamp
< INITIALIZATION_FORGIVENESS_WINDOW;
if (isRecentlyInitialized) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 1f8d365cfdad..698fed33a408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -89,9 +89,9 @@ class NotifCollectionCache<V>(
return true
}
- // Using uptimeMillis since it's guaranteed to be monotonic, as we don't want a
+ // Using elapsedRealtime since it's guaranteed to be monotonic, as we don't want a
// timezone/clock change to break us
- val now = systemClock.uptimeMillis()
+ val now = UseElapsedRealtimeForCreationTime.getCurrentTime(systemClock)
// Cannot purge the same entry from two threads simultaneously
synchronized(key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d031d831bf5a..4558017a98c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -251,7 +251,7 @@ public final class NotificationEntry extends ListEntry {
/**
* @param sbn the StatusBarNotification from system server
* @param ranking also from system server
- * @param creationTime SystemClock.uptimeMillis of when we were created
+ * @param creationTime SystemClock.elapsedRealtime of when we were created
*/
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@@ -508,7 +508,7 @@ public final class NotificationEntry extends ListEntry {
ArrayList<NotificationEntry> children = new ArrayList<>();
for (ExpandableNotificationRow child : rowChildren) {
- children.add(child.getEntry());
+ children.add(child.getEntryLegacy());
}
return children;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 1168c647c26a..345b6aae9673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -137,6 +137,10 @@ class NotificationEntryAdapter(
return entry.ranking.isAmbient
}
+ override fun isPromotedOngoing(): Boolean {
+ return entry.isPromotedOngoing
+ }
+
override fun isFullScreenCapable(): Boolean {
return entry.sbn.notification.fullScreenIntent != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 872cd68e1b21..e9c4efc4de64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -101,4 +101,13 @@ public abstract class PipelineEntry {
public void setBucket(@PriorityBucket int bucket) {
mBucket = bucket;
}
+
+ /**
+ * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
+ * fresh attach state (all entries will be null/default-initialized).
+ */
+ void beginNewAttachState() {
+ mPreviousAttachState.clone(mAttachState);
+ mAttachState.reset();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 238ba8d9f490..5cea82140692 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -289,6 +289,9 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mIdToBundleEntry.clear();
for (String id: mNotifBundler.getBundleIds()) {
+ if (BundleCoordinator.debugBundleUi) {
+ Log.i(TAG, "create BundleEntry with id: " + id);
+ }
mIdToBundleEntry.put(id, new BundleEntry(id));
}
}
@@ -562,6 +565,11 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
entry.beginNewAttachState();
}
+ for (BundleEntry be : mIdToBundleEntry.values()) {
+ be.beginNewAttachState();
+ // TODO(b/399736937) Clear bundle children
+ // BundleEntry has not representative summary so we do not need to clear it here.
+ }
mNotifList.clear();
}
@@ -570,7 +578,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
- final long now = mSystemClock.uptimeMillis();
+ final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
final GroupEntry groupEntry = (GroupEntry) entry;
@@ -614,7 +622,8 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
GroupEntry group = mGroups.get(topLevelKey);
if (group == null) {
- group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
+ group = new GroupEntry(topLevelKey,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock));
mGroups.put(topLevelKey, group);
}
if (group.getParent() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
new file mode 100644
index 000000000000..23f90f3694a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Flags
+import com.android.systemui.util.time.SystemClock
+
+/** A helper class for replacing uptimeMillis with elapsedRealtime for entry creation times */
+public object UseElapsedRealtimeForCreationTime {
+ @JvmStatic
+ fun getCurrentTime(clock: SystemClock): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return clock.elapsedRealtime()
+ }
+ return clock.uptimeMillis()
+ }
+
+ @JvmStatic
+ fun getCurrentTime(): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return android.os.SystemClock.elapsedRealtime()
+ }
+ return android.os.SystemClock.uptimeMillis()
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2eec68b26347..fb7772e26240 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -25,7 +25,7 @@ import java.util.List;
* Represents a set of notification post events for a particular notification group.
*/
public class EventBatch {
- /** SystemClock.uptimeMillis() */
+ /** SystemClock.elapsedRealtime() */
final long mCreatedTimestamp;
/** SBN.getGroupKey -- same for all members */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 96b35428b3ce..944e313d795a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -182,11 +183,12 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
private void maybeEmitBatch(StatusBarNotification sbn) {
final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
final EventBatch batch = mBatches.get(sbn.getGroupKey());
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
if (event != null) {
mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
emitBatch(requireNonNull(event.getBatch()));
} else if (batch != null
- && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+ && now - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
emitBatch(batch);
}
@@ -228,7 +230,8 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
private EventBatch getOrBuildBatch(final String groupKey) {
EventBatch batch = mBatches.get(groupKey);
if (batch == null) {
- batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+ batch = new EventBatch(UseElapsedRealtimeForCreationTime.getCurrentTime(mClock),
+ groupKey);
mBatches.put(groupKey, batch);
}
return batch;
@@ -268,7 +271,8 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
}
events.sort(mEventComparator);
- long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+ long batchAge = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock)
+ - batch.mCreatedTimestamp;
mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
mHandler.onNotificationBatchPosted(events);
@@ -298,7 +302,7 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- long now = mClock.uptimeMillis();
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
int eventCount = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 8833ff1ce20c..4478d0e97920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -98,9 +98,14 @@ class BundleCoordinator @Inject constructor(
object : NotifBundler("NotifBundler") {
// Use list instead of set to keep fixed order
- override val bundleIds: List<String> = SYSTEM_RESERVED_IDS
+ override val bundleIds: List<String> =
+ if (debugBundleUi) SYSTEM_RESERVED_IDS + "notify"
+ else SYSTEM_RESERVED_IDS
override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+ if (debugBundleUi && entry?.key?.contains("notify") == true) {
+ return "notify"
+ }
return entry?.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
}
}
@@ -110,4 +115,9 @@ class BundleCoordinator @Inject constructor(
pipeline.setNotifBundler(bundler)
}
}
+
+ companion object {
+ @kotlin.jvm.JvmField
+ var debugBundleUi: Boolean = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index b54f21b23bba..1be415d7bf47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -241,8 +241,7 @@ public class PreparationCoordinator implements Coordinator {
isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
}
-
- return !isInflated(entry) || isMemberOfDelayedGroup;
+ return !isInflated(entry) || (isMemberOfDelayedGroup != null && isMemberOfDelayedGroup);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index a0a86710b4ba..f43767d3effb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,7 +22,6 @@ import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -147,9 +146,7 @@ internal constructor(
traceSection("updateNotifOnUiModeChanged") {
mPipeline?.allNotifs?.forEach { entry ->
entry.row?.onUiModeChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- }
+ mGutsManager.closeAndUndoGuts()
}
}
}
@@ -158,16 +155,7 @@ internal constructor(
colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- } else {
- // This property actually gets reset when the guts are re-inflated, so we're never
- // actually calling onDensityOrFontScaleChanged below.
- val exposedGuts = entry.areGutsExposed()
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry)
- }
- }
+ mGutsManager.closeAndUndoGuts()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index bdbdc53c4b1c..0466c0359710 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,9 +39,9 @@ import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -113,6 +113,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
@VisibleForTesting
protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
+ private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
+
@Inject
public VisualStabilityCoordinator(
@Background DelayableExecutor delayableExecutor,
@@ -182,7 +184,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
this::onTrackingHeadsUpModeChanged);
}
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
if (SceneContainerFlag.isEnabled()) {
mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
@@ -437,7 +439,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
boolean wasReorderingAllowed = mReorderingAllowed;
// No need to run notification pipeline when the lockscreen is in fading animation.
mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
- || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
+ || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
mReorderingAllowed = isReorderingAllowed();
if (wasPipelineRunAllowed != mPipelineRunAllowed
|| wasReorderingAllowed != mReorderingAllowed) {
@@ -499,7 +501,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
* notification and we are reordering based on the user's change.
*
* @param entry notification entry that can change sections even if isReorderingAllowed is false
- * @param now current time SystemClock.uptimeMillis
+ * @param now current time SystemClock.elapsedRealtime
*/
public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
final String entryKey = entry.getKey();
@@ -566,7 +568,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing);
pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity);
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
pw.println(" lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
}
pw.println("reorderingAllowed: " + mReorderingAllowed);
@@ -627,7 +629,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
}
private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
- if (!Flags.checkLockscreenGoneTransition()) {
+ if (!mCheckLockScreenTransitionEnabled) {
return;
}
if (inGoneTransition == mLockscreenInGoneTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 07fa6aeb7900..03b4076ba6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -20,7 +20,6 @@ import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_N
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import android.os.SystemClock;
import android.service.notification.NotificationStats;
import androidx.annotation.NonNull;
@@ -30,6 +29,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,7 +85,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
public void onImportanceChanged(NotificationEntry entry) {
mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
entry,
- SystemClock.uptimeMillis());
+ UseElapsedRealtimeForCreationTime.getCurrentTime());
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 776c7d5eb7f6..389bb3129c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -41,8 +41,8 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
* this entry will not have any grouping nor sorting information.
* If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
* this entry will have grouping and sorting information.
- * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
- * pipeline execution. This value will be the same for all pluggable calls made
+ * @param now A timestamp in SystemClock.elapsedRealtime that represents "now" for the purposes
+ * of pipeline execution. This value will be the same for all pluggable calls made
* during this pipeline run, giving pluggables a stable concept of "now" to compare
* various entries against.
* @return True if the notif should be removed from the list
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 8021d8f58ccc..a552ca554fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -234,21 +234,21 @@ private class ShadeNode(val controller: NodeController) {
fun getChildCount(): Int = controller.getChildCount()
fun addChildAt(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#addChildAt") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#addChildAt" }) {
controller.addChildAt(child.controller, index)
child.controller.onViewAdded()
}
}
fun moveChildTo(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#moveChildTo") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#moveChildTo" }) {
controller.moveChildTo(child.controller, index)
child.controller.onViewMoved()
}
}
fun removeChild(child: ShadeNode, isTransfer: Boolean) {
- traceSection("ShadeNode#removeChild") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#removeChild" }) {
controller.removeChild(child.controller, isTransfer)
child.controller.onViewRemoved()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index cdbe0fd23a9a..8d1e61123fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -81,7 +81,7 @@ constructor(
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
- activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }
+ activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }.distinctUntilChanged()
}
}
@@ -90,9 +90,9 @@ constructor(
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
- activeHeadsUpRows.map {
- it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet()
- }
+ activeHeadsUpRows
+ .map { it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet() }
+ .distinctUntilChanged() // TODO(b/402428276) stop sending duplicate updates instead
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ec8fbc08de7a..5bdd769dfa03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -19,7 +19,6 @@ import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -112,7 +112,7 @@ public class NotificationLogger implements StateListener, CoreStartable,
@Override
public void run() {
- mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+ mLastVisibilityReportUptimeMs = UseElapsedRealtimeForCreationTime.getCurrentTime();
// 1. Loop over active entries:
// A. Keep list of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 2a01a14f56aa..777392df67cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -19,19 +19,26 @@ package com.android.systemui.statusbar.notification.promoted
import android.app.Flags
import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
+import android.content.Context
import android.graphics.PorterDuff
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.View.MeasureSpec.makeMeasureSpec
import android.view.View.VISIBLE
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewStub
import android.widget.Chronometer
import android.widget.DateTimeView
+import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
+import androidx.annotation.DimenRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
@@ -42,7 +49,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
@@ -88,22 +97,12 @@ fun AODPromotedNotification(
}
key(content.identity) {
- val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
- val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
-
- val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
-
- val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
- val borderShape = RoundedCornerShape(borderRadius)
-
- Box(modifier = modifier.padding(sidePaddingValues)) {
- AODPromotedNotificationView(
- layoutResource = layoutResource,
- content = content,
- audiblyAlertedIconVisible = audiblyAlertedIconVisible,
- modifier = Modifier.border(borderStroke, borderShape),
- )
- }
+ AODPromotedNotificationView(
+ layoutResource = layoutResource,
+ content = content,
+ audiblyAlertedIconVisible = audiblyAlertedIconVisible,
+ modifier = modifier,
+ )
}
}
@@ -114,27 +113,91 @@ fun AODPromotedNotificationView(
audiblyAlertedIconVisible: Boolean,
modifier: Modifier = Modifier,
) {
- AndroidView(
- factory = { context ->
- val view =
- traceSection("$TAG.inflate") {
- LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
- }
-
- val updater =
- traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
-
- view.setTag(viewUpdaterTagId, updater)
-
- view
- },
- update = { view ->
- val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
-
- traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
- },
- modifier = modifier,
- )
+ val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+ val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
+
+ val boxModifier = modifier.padding(sidePaddingValues)
+
+ val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
+
+ val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+ val borderShape = RoundedCornerShape(borderRadius)
+
+ val maxHeight =
+ with(LocalDensity.current) {
+ scaledFontHeight(systemuiR.dimen.notification_max_height_for_promoted_ongoing)
+ .toPx()
+ }
+ .toInt()
+
+ val viewModifier = Modifier.border(borderStroke, borderShape)
+
+ Box(modifier = boxModifier) {
+ AndroidView(
+ factory = { context ->
+ val notif =
+ traceSection("$TAG.inflate") {
+ LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+ }
+ val updater =
+ traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) }
+
+ val frame = FrameLayoutWithMaxHeight(maxHeight, context)
+ frame.addView(notif)
+ frame.setTag(viewUpdaterTagId, updater)
+
+ frame
+ },
+ update = { frame ->
+ val updater = frame.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+ traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
+ frame.maxHeight = maxHeight
+ },
+ modifier = viewModifier,
+ )
+ }
+}
+
+private class FrameLayoutWithMaxHeight(maxHeight: Int, context: Context) : FrameLayout(context) {
+ var maxHeight = maxHeight
+ set(value) {
+ if (field != value) {
+ field = value
+ requestLayout()
+ }
+ }
+
+ // This mirrors the logic in NotificationContentView.onMeasure.
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (childCount < 1) {
+ return
+ }
+
+ val child = getChildAt(0)
+ val childLayoutHeight = child.layoutParams.height
+ val childHeightSpec =
+ if (childLayoutHeight >= 0) {
+ makeMeasureSpec(maxHeight.coerceAtMost(childLayoutHeight), EXACTLY)
+ } else {
+ makeMeasureSpec(maxHeight, AT_MOST)
+ }
+ measureChildWithMargins(child, widthMeasureSpec, 0, childHeightSpec, 0)
+ val childMeasuredHeight = child.measuredHeight
+
+ val ownHeightMode = MeasureSpec.getMode(heightMeasureSpec)
+ val ownHeightSize = MeasureSpec.getSize(heightMeasureSpec)
+
+ val ownMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+ val ownMeasuredHeight =
+ if (ownHeightMode != UNSPECIFIED) {
+ childMeasuredHeight.coerceAtMost(ownHeightSize)
+ } else {
+ childMeasuredHeight
+ }
+
+ setMeasuredDimension(ownMeasuredWidth, ownMeasuredHeight)
+ }
}
private val PromotedNotificationContentModel.layoutResource: Int?
@@ -521,6 +584,11 @@ private enum class AodPromotedNotificationColor(val colorInt: Int) {
val brush = SolidColor(androidx.compose.ui.graphics.Color(colorInt))
}
+@Composable
+private fun scaledFontHeight(@DimenRes dimenId: Int): Dp {
+ return dimensionResource(dimenId) * LocalDensity.current.fontScale.coerceAtLeast(1f)
+}
+
private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
private const val TAG = "AODPromotedNotification"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index 2aafe8c81381..d35c3b617246 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -20,6 +20,7 @@ import android.app.Notification
import android.app.Notification.BigPictureStyle
import android.app.Notification.BigTextStyle
import android.app.Notification.CallStyle
+import android.app.Notification.EXTRA_BIG_TEXT
import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
import android.app.Notification.EXTRA_PROGRESS
import android.app.Notification.EXTRA_PROGRESS_INDETERMINATE
@@ -27,8 +28,10 @@ import android.app.Notification.EXTRA_PROGRESS_MAX
import android.app.Notification.EXTRA_SUB_TEXT
import android.app.Notification.EXTRA_TEXT
import android.app.Notification.EXTRA_TITLE
+import android.app.Notification.EXTRA_TITLE_BIG
import android.app.Notification.EXTRA_VERIFICATION_ICON
import android.app.Notification.EXTRA_VERIFICATION_TEXT
+import android.app.Notification.InboxStyle
import android.app.Notification.ProgressStyle
import android.content.Context
import android.graphics.drawable.Icon
@@ -105,8 +108,8 @@ constructor(
contentBuilder.shortCriticalText = notification.shortCriticalText()
contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
- contentBuilder.title = notification.title()
- contentBuilder.text = notification.text()
+ contentBuilder.title = notification.resolveTitle(recoveredBuilder.style)
+ contentBuilder.text = notification.resolveText(recoveredBuilder.style)
contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider)
contentBuilder.oldProgress = notification.oldProgress()
@@ -127,8 +130,39 @@ constructor(
private fun Notification.title(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE)
+ private fun Notification.bigTitle(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE_BIG)
+
+ private fun Notification.Style.bigTitleOverridesTitle(): Boolean {
+ return when (this) {
+ is BigTextStyle,
+ is BigPictureStyle,
+ is InboxStyle -> true
+ else -> false
+ }
+ }
+
+ private fun Notification.resolveTitle(style: Notification.Style?): CharSequence? {
+ return if (style?.bigTitleOverridesTitle() == true) {
+ bigTitle()
+ } else {
+ null
+ } ?: title()
+ }
+
private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_TEXT)
+ private fun Notification.bigText(): CharSequence? = extras?.getCharSequence(EXTRA_BIG_TEXT)
+
+ private fun Notification.Style.bigTextOverridesText(): Boolean = this is BigTextStyle
+
+ private fun Notification.resolveText(style: Notification.Style?): CharSequence? {
+ return if (style?.bigTextOverridesText() == true) {
+ bigText()
+ } else {
+ null
+ } ?: text()
+ }
+
private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT)
private fun Notification.shortCriticalText(): String? {
@@ -233,13 +267,13 @@ constructor(
private fun BigPictureStyle.extractContent(
contentBuilder: PromotedNotificationContentModel.Builder
) {
- // TODO?
+ // Big title is handled in resolveTitle, and big picture is unsupported.
}
private fun BigTextStyle.extractContent(
contentBuilder: PromotedNotificationContentModel.Builder
) {
- // TODO?
+ // Big title and big text are handled in resolveTitle and resolveText.
}
private fun CallStyle.extractContent(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
new file mode 100644
index 000000000000..14295357c54f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class which can receive both a demotion signal and a single handler of that signal */
+@SysUISingleton
+class PackageDemotionInteractor @Inject constructor() {
+ private var demotionSignalHandler: ((packageName: String, uid: Int) -> Unit)? = null
+
+ /**
+ * called after sending a the demotion signal to
+ * [android.app.INotificationManager.setCanBePromoted]
+ */
+ fun onPackageDemoted(packageName: String, uid: Int) {
+ demotionSignalHandler?.invoke(packageName, uid)
+ }
+
+ /** sets the [handler] that will be called when [onPackageDemoted] is called. */
+ fun setOnPackageDemotionHandler(handler: (packageName: String, uid: Int) -> Unit) {
+ demotionSignalHandler = handler
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 689222608abe..e76867373139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -40,6 +40,7 @@ import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
+import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
@@ -101,7 +102,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
- private int mNormalColor;
+ protected int mNormalColor;
+ protected int mOpaqueColor;
private boolean mIsBelowSpeedBump;
private long mLastActionUpTime;
@@ -130,17 +132,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected void updateColors() {
if (notificationRowTransparency()) {
- if (mIsBlurSupported) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
- } else {
- mNormalColor = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainer);
- }
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ mOpaqueColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
- setBackgroundToNormalColor();
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
@@ -151,12 +149,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mOverrideAmount = 0.0f;
}
- private void setBackgroundToNormalColor() {
- if (mBackgroundNormal != null) {
- mBackgroundNormal.setNormalColor(mNormalColor);
- }
- }
-
/**
* Reload background colors from resources and invalidate views.
*/
@@ -186,7 +178,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBackgroundNormal = findViewById(R.id.backgroundNormal);
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
- setBackgroundToNormalColor();
initBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -352,7 +343,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
protected boolean usesTransparentBackground() {
- return mIsBlurSupported && notificationRowTransparency();
+ return false;
}
@Override
@@ -709,7 +700,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (withTint && mBgTint != NO_COLOR) {
return mBgTint;
} else {
- return mNormalColor;
+ if (Flags.notificationRowTransparency()) {
+ return usesTransparentBackground() ? mNormalColor : mOpaqueColor;
+ } else {
+ return mNormalColor;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8da2f768bf71..2c3676ae16ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -82,6 +82,7 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -421,9 +422,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
onExpansionChanged(true /* userAction */, wasExpanded);
} else {
- final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
- boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ final boolean wasExpanded =
+ mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(getEntryLegacy());
+ mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
if (shouldLogExpandClickMetric) {
mMetricsLogger.action(
MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
@@ -453,7 +455,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
} else {
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
}
if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
@@ -558,7 +560,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
return mKey;
} else {
- return mEntry.getKey();
+ return getEntryLegacy().getKey();
}
}
@@ -661,14 +663,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public boolean getIsNonblockable() {
NotificationBundleUi.assertInLegacyMode();
- if (mEntry == null) {
+ if (getEntryLegacy() == null) {
return true;
}
- return !mEntry.isBlockable();
+ return !getEntryLegacy().isBlockable();
}
private boolean isConversation() {
- return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry)
+ return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntry())
!= PeopleNotificationIdentifier.TYPE_NON_PERSON;
}
@@ -679,7 +681,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)");
}
for (NotificationContentView l : mLayouts) {
- l.onNotificationUpdated(mEntry);
+ l.onNotificationUpdated(getEntry());
}
mShowingPublicInitialized = false;
if (mMenuRow != null) {
@@ -746,7 +748,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void updateBubbleButton() {
for (NotificationContentView l : mLayouts) {
- l.updateBubbleButton(mEntry);
+ l.updateBubbleButton(getEntry());
}
}
@@ -777,7 +779,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
getBackgroundColorWithoutTint());
} else {
- return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ return getEntryLegacy().getContrastedColor(mContext, mIsMinimized && !isExpanded(),
getBackgroundColorWithoutTint());
}
}
@@ -885,7 +887,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
targetSdk = mEntryAdapter.getTargetSdk();
} else {
- targetSdk = mEntry.targetSdk;
+ targetSdk = getEntryLegacy().targetSdk;
}
boolean beforeN = targetSdk < Build.VERSION_CODES.N;
@@ -901,7 +903,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
summarization = mEntryAdapter.getSummarization();
} else {
- summarization = mEntry.getRanking().getSummarization();
+ summarization = getEntryLegacy().getRanking().getSummarization();
}
if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -945,7 +947,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
}
+ /**
+ * Check {@link NotificationBundleUi#isEnabled()}
+ * and use {@link #getEntryAdapter()} when true
+ * and {@link #getEntryLegacy()} when false.
+ */
+ @NonNull
+ @Deprecated
+ public NotificationEntry getEntryLegacy() {
+ NotificationBundleUi.assertInLegacyMode();
+ return mEntry;
+ }
+
+ /**
+ * Check {@link NotificationBundleUi#isEnabled()}
+ * and use {@link #getEntryAdapter()} when true
+ * and {@link #getEntryLegacy()} when false.
+ */
@NonNull
+ @Deprecated
public NotificationEntry getEntry() {
return mEntry;
}
@@ -979,7 +999,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
- updateColors();
+ updateBackgroundTint();
}
/**
@@ -1481,8 +1501,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void setBubbleClickListener(@Nullable OnClickListener l) {
mBubbleClickListener = l;
// ensure listener is passed to the content views
- mPrivateLayout.updateBubbleButton(mEntry);
- mPublicLayout.updateBubbleButton(mEntry);
+ mPrivateLayout.updateBubbleButton(getEntry());
+ mPublicLayout.updateBubbleButton(getEntry());
}
/**
@@ -1554,7 +1574,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return initializationTime != -1
&& SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
} else {
- return getEntry().hasFinishedInitialization();
+ return getEntryLegacy().hasFinishedInitialization();
}
}
@@ -1633,14 +1653,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
mEntryAdapter.prepareForInflation();
} else {
- mEntry.getSbn().clearPackageContext();
+ getEntryLegacy().getSbn().clearPackageContext();
}
// TODO: Move content inflation logic out of this call
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
- ? mEntryAdapter.getKey() : mEntry.getKey());
+ ? mEntryAdapter.getKey() : getEntryLegacy().getKey());
mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
Trace.endSection();
}
@@ -1678,21 +1698,36 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void setBackgroundTintColor(int color) {
- super.setBackgroundTintColor(color);
- NotificationContentView view = getShowingLayout();
- if (view != null) {
- view.setBackgroundTintColor(color);
- }
- if (notificationRowTransparency() && mBackgroundNormal != null) {
- if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) {
- mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
+ if (notificationRowTransparency()) {
+ boolean isColorized = false;
+ if (NotificationBundleUi.isEnabled()) {
+ if (mEntryAdapter != null) {
+ isColorized = mEntryAdapter.isColorized();
+ }
} else {
if (mEntry != null) {
- mBackgroundNormal.setBgIsColorized(
- mEntry.getSbn().getNotification().isColorized());
+ isColorized = mEntry.getSbn().getNotification().isColorized();
+ }
+ }
+ boolean isTransparent = usesTransparentBackground();
+ if (isColorized) {
+ // For colorized notifications, use a color that matches the tint color at 90% alpha
+ // when the row is transparent.
+ color = ColorUtils.setAlphaComponent(
+ color, (int) (0xFF * (isTransparent ? 0.9f : 1)));
+ } else {
+ // For non-colorized notifications, use the semi-transparent normal color token
+ // when the row is transparent, and the opaque color token otherwise.
+ if (!isTransparent && mBgTint == NO_COLOR) {
+ color = mOpaqueColor;
}
}
}
+ super.setBackgroundTintColor(color);
+ NotificationContentView view = getShowingLayout();
+ if (view != null) {
+ view.setBackgroundTintColor(color);
+ }
}
public void closeRemoteInput() {
@@ -2310,7 +2345,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@VisibleForTesting
- protected void setEntry(NotificationEntry entry) {
+ @Deprecated
+ protected void setEntryLegacy(NotificationEntry entry) {
+ NotificationBundleUi.assertInLegacyMode();
mEntry = entry;
}
@@ -2403,7 +2440,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
return traceTag + "(" + getEntryAdapter().getStyle() + ")";
} else {
- return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+ return traceTag + "(" + getEntryLegacy().getNotificationStyle() + ")";
}
}
@@ -2908,7 +2945,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
return getEntryAdapter().getIcons().getShelfIcon();
} else {
- return mEntry.getIcons().getShelfIcon();
+ return getEntryLegacy().getIcons().getShelfIcon();
}
}
@@ -3016,7 +3053,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
}
} else {
- mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
+ mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), userExpanded);
}
onExpansionChanged(true /* userAction */, wasExpanded);
return;
@@ -3113,7 +3150,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setOnKeyguard(onKeyguard);
}
}
- updateColors();
+ updateBackgroundTint();
}
}
@@ -3159,7 +3196,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public boolean canShowHeadsUp() {
boolean canEntryHun = NotificationBundleUi.isEnabled()
? mEntryAdapter.canPeek()
- : mEntry.isStickyAndNotDemoted();
+ : getEntryLegacy().isStickyAndNotDemoted();
if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
(!canEntryHun
|| (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
@@ -3181,13 +3218,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
return mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
}
- return mGroupExpansionManager.isGroupExpanded(mEntry);
+ return mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
}
private boolean isGroupRoot() {
return NotificationBundleUi.isEnabled()
? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
- : mGroupMembershipManager.isGroupSummary(mEntry);
+ : mGroupMembershipManager.isGroupSummary(getEntryLegacy());
}
private void onAttachedChildrenCountChanged() {
@@ -3209,7 +3246,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
} else {
- mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ mPublicLayout.setNotificationWhen(
+ getEntryLegacy().getSbn().getNotification().getWhen());
}
}
getShowingLayout().updateBackgroundColor(false /* animate */);
@@ -3243,12 +3281,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return false;
}
- final NotificationEntry entry = mEntry;
- if (entry == null) {
- return false;
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = mEntryAdapter;
+ if (entryAdapter == null) {
+ return false;
+ }
+ return entryAdapter.isPromotedOngoing();
+ } else {
+ final NotificationEntry entry = mEntry;
+ if (entry == null) {
+ return false;
+ }
+ return entry.isPromotedOngoing();
}
-
- return entry.isPromotedOngoing();
}
private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
@@ -3537,7 +3582,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mEntryAdapter.isClearable()
&& (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
} else {
- return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ return getEntryLegacy().isClearable()
+ && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
}
@@ -3551,7 +3597,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (!NotificationBundleUi.isEnabled()) {
// this is only called if row.getParent() instanceof NotificationStackScrollLayout,
// so there is never a group to expand
- mGroupExpansionManager.setGroupExpanded(mEntry, true);
+ mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), true);
}
}
notifyHeightChanged(/* needsAnimation= */ false);
@@ -3784,7 +3830,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return true;
}
} else {
- if (getEntry().getSbn().getNotification().isColorized()) {
+ if (getEntryLegacy().getSbn().getNotification().isColorized()) {
return true;
}
}
@@ -4255,7 +4301,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public boolean isMediaRow() {
NotificationBundleUi.assertInLegacyMode();
- return mEntry.getSbn().getNotification().isMediaNotification();
+ return getEntryLegacy().getSbn().getNotification().isMediaNotification();
}
public void setAboveShelf(boolean aboveShelf) {
@@ -4377,11 +4423,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
// Skip super call; dump viewState ourselves
- if (NotificationBundleUi.isEnabled()) {
- pw.println("Notification: " + mEntryAdapter.getKey());
- } else {
- pw.println("Notification: " + mEntry.getKey());
- }
+ pw.println("Notification: " + getKey());
DumpUtilsKt.withIncreasedIndent(pw, () -> {
pw.println(this);
pw.print("visibility: " + getVisibility());
@@ -4618,7 +4660,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
mLaunchAnimationRunning = launchAnimationRunning;
} else {
- getEntry().setExpandAnimationRunning(launchAnimationRunning);
+ getEntryLegacy().setExpandAnimationRunning(launchAnimationRunning);
}
}
@@ -4627,12 +4669,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (NotificationBundleUi.isEnabled()) {
return mLaunchAnimationRunning;
} else {
- return getEntry().isExpandAnimationRunning();
+ return getEntryLegacy().isExpandAnimationRunning();
}
}
@Override
protected boolean usesTransparentBackground() {
- return super.usesTransparentBackground() && !mIsHeadsUp && !mOnKeyguard;
+ // Row background should be opaque when it's displayed as a heads-up notification or
+ // displayed on keyguard.
+ // TODO(b/388891313): Account for isBlurSupported when it is initialized and updated
+ // correctly.
+ return notificationRowTransparency() && !mIsHeadsUp && !mOnKeyguard;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index ac55930f5c11..7c0ee6685e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -139,7 +139,7 @@ public class ExpandableNotificationRowController implements NotifViewController
}
final int viewUserId = NotificationBundleUi.isEnabled()
? mView.getEntryAdapter().getSbn().getUserId()
- : mView.getEntry().getSbn().getUserId();
+ : mView.getEntryLegacy().getSbn().getUserId();
if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
mView.getPrivateLayout().setBubblesEnabledForUser(
BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -395,7 +395,7 @@ public class ExpandableNotificationRowController implements NotifViewController
mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
} else {
- mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ mView.getEntryLegacy().setInitializationTime(mClock.elapsedRealtime());
mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
mPluginManager.addPluginListener(mView,
@@ -429,7 +429,9 @@ public class ExpandableNotificationRowController implements NotifViewController
@Override
@NonNull
public String getNodeLabel() {
- return NotificationBundleUi.isEnabled() ? mView.getLoggingKey() : logKey(mView.getEntry());
+ return NotificationBundleUi.isEnabled()
+ ? mView.getLoggingKey()
+ : logKey(mView.getEntryLegacy());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 9ae2eb1b9328..20b826a3ca92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -109,7 +109,7 @@ public class ExpandableNotificationRowDragController {
StatusBarNotification sn = NotificationBundleUi.isEnabled()
? enr.getEntryAdapter().getSbn()
- : enr.getEntry().getSbn();
+ : enr.getEntryLegacy().getSbn();
Notification notification = sn.getNotification();
final PendingIntent contentIntent = notification.contentIntent != null
? notification.contentIntent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index d02f636728fc..fe3a856e711e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.notification.row
import android.animation.ValueAnimator
import android.content.Context
+import android.content.res.ColorStateList
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.Path
@@ -31,9 +33,27 @@ import android.graphics.RectF
import android.graphics.Shader
import com.android.systemui.res.R
import com.android.wm.shell.shared.animation.Interpolators
+import android.graphics.drawable.RippleDrawable
+import androidx.core.content.ContextCompat
class MagicActionBackgroundDrawable(
context: Context,
+) : RippleDrawable(
+ ContextCompat.getColorStateList(
+ context,
+ R.color.notification_ripple_untinted_color
+ ) ?: ColorStateList.valueOf(Color.TRANSPARENT),
+ createBaseDrawable(context), null
+) {
+ companion object {
+ private fun createBaseDrawable(context: Context): Drawable {
+ return BaseBackgroundDrawable(context)
+ }
+ }
+}
+
+class BaseBackgroundDrawable(
+ context: Context,
) : Drawable() {
private val cornerRadius = context.resources.getDimension(R.dimen.magic_action_button_corner_radius)
@@ -42,6 +62,14 @@ class MagicActionBackgroundDrawable(
private val buttonShape = Path()
// Color and style
+ private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ val bgColor =
+ context.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh
+ )
+ color = bgColor
+ style = Paint.Style.FILL
+ }
private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val outlineColor =
context.getColor(
@@ -91,6 +119,7 @@ class MagicActionBackgroundDrawable(
canvas.save()
// Draw background
canvas.clipPath(buttonShape)
+ canvas.drawPath(buttonShape, bgPaint)
// Apply gradient to outline
canvas.drawPath(buttonShape, outlinePaint)
updateGradient(boundsF)
@@ -119,11 +148,13 @@ class MagicActionBackgroundDrawable(
}
override fun setAlpha(alpha: Int) {
+ bgPaint.alpha = alpha
outlinePaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
+ bgPaint.colorFilter = colorFilter
outlinePaint.colorFilter = colorFilter
invalidateSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4914e1073059..e4997e4f53ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,9 +36,9 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dumpable;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
import com.android.systemui.util.DrawableDumpKt;
@@ -52,7 +52,6 @@ import java.util.Arrays;
public class NotificationBackgroundView extends View implements Dumpable,
ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
- private static final int MAX_ALPHA = 0xFF;
private final boolean mDontModifyCorners;
private Drawable mBackground;
private int mClipTopAmount;
@@ -73,8 +72,6 @@ public class NotificationBackgroundView extends View implements Dumpable,
private final ColorStateList mLightColoredStatefulColors;
private final ColorStateList mDarkColoredStatefulColors;
private int mNormalColor;
- private boolean mBgIsColorized = false;
- private boolean mForceOpaque = false;
private final int convexR = 9;
private final int concaveR = 22;
@@ -88,13 +85,15 @@ public class NotificationBackgroundView extends View implements Dumpable,
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
+ if (notificationRowTransparency()) {
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ } else {
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
+ }
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
- public void setNormalColor(int color) {
- mNormalColor = color;
- }
-
@Override
public void onTargetVisibilityChanged(boolean targetVisible) {
if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
@@ -140,21 +139,6 @@ public class NotificationBackgroundView extends View implements Dumpable,
}
}
- /**
- * A way to tell whether the background has been colorized.
- */
- public boolean isColorized() {
- return mBgIsColorized;
- }
-
- /**
- * A way to inform this class whether the background has been colorized.
- * We need to know this, in order to *not* override that color.
- */
- public void setBgIsColorized(boolean b) {
- mBgIsColorized = b;
- }
-
private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
// TODO(b/365585705): Adapt to RTL after the UX design is finalized.
@@ -311,28 +295,21 @@ public class NotificationBackgroundView extends View implements Dumpable,
return ((LayerDrawable) mBackground).getDrawable(1);
}
- private void updateBaseLayerColor() {
- // BG base layer being a drawable, there isn't a method like setColor() to color it.
- // Instead, we set a color filter that essentially replaces every pixel of the drawable.
- // For non-colorized notifications, this function specifies a new color token.
- // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
- int color = isColorized()
- ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
- : mNormalColor;
- getBaseBackgroundLayer().setColorFilter(
- new PorterDuffColorFilter(
- color,
- PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
- }
-
public void setTint(int tintColor) {
Drawable baseLayer = getBaseBackgroundLayer();
- baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
- baseLayer.setTint(tintColor);
- mTintColor = tintColor;
if (notificationRowTransparency()) {
- updateBaseLayerColor();
+ // BG base layer being a drawable, there isn't a method like setColor() to color it.
+ // Instead, we set a color filter that essentially replaces every pixel of the drawable.
+ baseLayer.setColorFilter(
+ new PorterDuffColorFilter(
+ tintColor,
+ // SRC operator discards the drawable's color+alpha
+ PorterDuff.Mode.SRC));
+ } else {
+ baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+ baseLayer.setTint(tintColor);
}
+ mTintColor = tintColor;
setStatefulColors();
invalidate();
}
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 ff4b835eb3c0..d97e25fdfa22 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
@@ -473,7 +473,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result.newPublicView = createSensitiveContentMessageNotification(
NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification()
- : row.getEntry().getSbn().getNotification(),
+ : row.getEntryLegacy().getSbn().getNotification(),
builder.getStyle(),
systemUiContext, packageContext).createContentView();
} else {
@@ -814,7 +814,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, row, callback, logger,
+ handleInflationError(runningInflations, e, row, entry, callback, logger,
"applying view synchronously");
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
@@ -836,7 +836,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
String invalidReason = isValidView(v, entry, row.getResources());
if (invalidReason != null) {
handleInflationError(runningInflations, new InflationException(invalidReason),
- row, callback, logger, "applied invalid view");
+ row, entry, callback, logger, "applied invalid view");
runningInflations.remove(inflationId);
return;
}
@@ -873,7 +873,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, row,
+ handleInflationError(runningInflations, e, row, entry,
callback, logger, "applying view");
}
}
@@ -969,13 +969,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private static void handleInflationError(
HashMap<Integer, CancellationSignal> runningInflations, Exception e,
- ExpandableNotificationRow row, @Nullable InflationCallback callback,
+ ExpandableNotificationRow row, NotificationEntry entry,
+ @Nullable InflationCallback callback,
NotificationRowContentBinderLogger logger, String logContext) {
Assert.isMainThread();
logger.logAsyncTaskException(row.getLoggingKey(), logContext, e);
runningInflations.values().forEach(CancellationSignal::cancel);
if (callback != null) {
- callback.handleInflationException(row.getEntry(), e);
+ callback.handleInflationException(entry, e);
}
}
@@ -1443,7 +1444,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
+ Integer.toHexString(sbn.getId());
Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
if (mCallback != null) {
- mCallback.handleInflationException(mRow.getEntry(),
+ mCallback.handleInflationException(mEntry,
new InflationException("Couldn't inflate contentViews" + e));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index cd6e4979ce88..26d318bea5cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -269,6 +269,7 @@ public class NotificationContentView extends FrameLayout implements Notification
mNotificationMaxHeight = maxHeight;
}
+ // This logic is mirrored in FrameLayoutWithMaxHeight.onMeasure in AODPromotedNotification.kt.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -600,7 +601,7 @@ public class NotificationContentView extends FrameLayout implements Notification
if (NotificationBundleUi.isEnabled()) {
return mContainingNotification.getEntryAdapter().getSbn();
} else {
- return mContainingNotification.getEntry().getSbn();
+ return mContainingNotification.getEntryLegacy().getSbn();
}
}
@@ -1247,7 +1248,7 @@ public class NotificationContentView extends FrameLayout implements Notification
final boolean isSingleLineViewPresent = mSingleLineView != null;
if (shouldShowSingleLineView && !isSingleLineViewPresent) {
- Log.wtf(TAG, "calculateVisibleType: SingleLineView is not available!");
+ Log.e(TAG, "calculateVisibleType: SingleLineView is not available!");
}
final int collapsedVisualType = shouldShowSingleLineView && isSingleLineViewPresent
@@ -1274,7 +1275,7 @@ public class NotificationContentView extends FrameLayout implements Notification
final boolean shouldShowSingleLineView = mIsChildInGroup && !isGroupExpanded();
final boolean isSingleLinePresent = mSingleLineView != null;
if (shouldShowSingleLineView && !isSingleLinePresent) {
- Log.wtf(TAG, "getVisualTypeForHeight: singleLineView is not available.");
+ Log.e(TAG, "getVisualTypeForHeight: singleLineView is not available.");
}
if (!mUserExpanding && shouldShowSingleLineView && isSingleLinePresent) {
@@ -1592,10 +1593,13 @@ public class NotificationContentView extends FrameLayout implements Notification
return;
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
- View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- ViewGroup actionListMarginTarget = layout.findViewById(
- com.android.internal.R.id.notification_action_list_margin_target);
- if (bubbleButton == null || actionContainer == null) {
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child visible/invisible instead.
+ View actionsContainerForVisibilityChange = layout.findViewById(
+ notificationsRedesignTemplates()
+ ? com.android.internal.R.id.actions_container_layout
+ : com.android.internal.R.id.actions_container);
+ if (bubbleButton == null || actionsContainerForVisibilityChange == null) {
return;
}
@@ -1613,17 +1617,14 @@ public class NotificationContentView extends FrameLayout implements Notification
bubbleButton.setImageDrawable(d);
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
bubbleButton.setVisibility(VISIBLE);
- actionContainer.setVisibility(VISIBLE);
- // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
- if (actionListMarginTarget != null) {
- removeBottomMargin(actionListMarginTarget);
- }
- if (notificationsRedesignTemplates()) {
- // Similar treatment for smart reply margin
- LinearLayout smartReplyContainer = layout.findViewById(
- com.android.internal.R.id.smart_reply_container);
- if (smartReplyContainer != null) {
- removeBottomMargin(smartReplyContainer);
+ actionsContainerForVisibilityChange.setVisibility(VISIBLE);
+ if (!notificationsRedesignTemplates()) {
+ // Set notification_action_list_margin_target's bottom margin to 0 when showing
+ // bubble
+ ViewGroup actionListMarginTarget = layout.findViewById(
+ com.android.internal.R.id.notification_action_list_margin_target);
+ if (actionListMarginTarget != null) {
+ removeBottomMargin(actionListMarginTarget);
}
}
} else {
@@ -1664,8 +1665,13 @@ public class NotificationContentView extends FrameLayout implements Notification
return;
}
ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
- View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- if (snoozeButton == null || actionContainer == null) {
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child visible/invisible instead.
+ View actionsContainerForVisibilityChange = layout.findViewById(
+ notificationsRedesignTemplates()
+ ? com.android.internal.R.id.actions_container_layout
+ : com.android.internal.R.id.actions_container);
+ if (snoozeButton == null || actionsContainerForVisibilityChange == null) {
return;
}
// Notification.Builder can 'disable' the snooze button to prevent it from being shown here
@@ -1691,7 +1697,7 @@ public class NotificationContentView extends FrameLayout implements Notification
snoozeButton.setOnClickListener(
mContainingNotification.getSnoozeClickListener(snoozeMenuItem));
snoozeButton.setVisibility(VISIBLE);
- actionContainer.setVisibility(VISIBLE);
+ actionsContainerForVisibilityChange.setVisibility(VISIBLE);
}
private void applySmartReplyView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9a75253295d5..cdb78d99538b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,6 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +70,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -100,6 +100,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
private final AccessibilityManager mAccessibilityManager;
private final HighPriorityProvider mHighPriorityProvider;
private final ChannelEditorDialogController mChannelEditorDialogController;
+ private final PackageDemotionInteractor mPackageDemotionInteractor;
private final OnUserInteractionCallback mOnUserInteractionCallback;
// Dependencies:
@@ -155,6 +156,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
LauncherApps launcherApps,
ShortcutManager shortcutManager,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
UserContextProvider contextTracker,
AssistantFeedbackController assistantFeedbackController,
Optional<BubblesManager> bubblesManagerOptional,
@@ -184,6 +186,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mShortcutManager = shortcutManager;
mContextTracker = contextTracker;
mChannelEditorDialogController = channelEditorDialogController;
+ mPackageDemotionInteractor = packageDemotionInteractor;
mAssistantFeedbackController = assistantFeedbackController;
mBubblesManagerOptional = bubblesManagerOptional;
mUiEventLogger = uiEventLogger;
@@ -231,15 +234,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
}
}
- public void onDensityOrFontScaleChanged(NotificationEntry entry) {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
- + " notificationUndoGutsOnConfigChanged is off");
- }
- setExposedGuts(entry.getGuts());
- bindGuts(entry.getRow());
- }
-
/**
* Sends an intent to open the notification settings for a particular package and optional
* channel.
@@ -291,11 +285,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
}
- private boolean bindGuts(final ExpandableNotificationRow row) {
- row.ensureGutsInflated();
- return bindGuts(row, mGutsMenuItem);
- }
-
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
@@ -429,6 +418,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
packageName,
row.getEntry().getChannel(),
row.getEntry(),
@@ -440,6 +430,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
NotificationBundleUi.isEnabled()
? !row.getEntry().isBlockable()
: row.getIsNonblockable(),
+ row.canViewBeDismissed(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
mMetricsLogger,
@@ -605,6 +596,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
return mNotificationGutsExposed;
}
+ @VisibleForTesting
public void setExposedGuts(NotificationGuts guts) {
mNotificationGutsExposed = guts;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 661122510c6c..b6f4ffce8e00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -74,6 +74,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -120,6 +121,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private boolean mIsAutomaticChosen;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
+ private boolean mIsDismissable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
@@ -160,6 +162,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mPressedApply = true;
mGutsContainer.closeControls(v, /* save= */ true);
};
+ private OnClickListener mOnCloseClickListener;
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +196,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -202,6 +206,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger,
@@ -226,11 +231,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mStartingChannelImportance = mSingleNotificationChannel.getImportance();
mWasShownHighPriority = wasShownHighPriority;
mIsNonblockable = isNonblockable;
+ mIsDismissable = isDismissable;
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
mUiEventLogger = uiEventLogger;
+ mOnCloseClickListener = onCloseClick;
mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
&& mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
@@ -277,6 +284,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
? VISIBLE : GONE);
+ View dismissButton = findViewById(R.id.inline_dismiss);
+ dismissButton.setOnClickListener(mOnCloseClickListener);
+ dismissButton.setVisibility(dismissButton.hasOnClickListeners() && mIsDismissable
+ ? VISIBLE : GONE);
+
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDismissSettings);
done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 2f94d3220dc8..ae52db88358a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -541,7 +541,7 @@ constructor(
val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id))
Log.e(TAG, "couldn't inflate view for notification $ident", e)
callback?.handleInflationException(
- if (NotificationBundleUi.isEnabled) entry else row.entry,
+ if (NotificationBundleUi.isEnabled) entry else row.entryLegacy,
InflationException("Couldn't inflate contentViews$e"),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 83897f5bc3a7..cec0ae696b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,7 +51,6 @@ import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
@@ -477,7 +476,7 @@ public class NotificationSnooze extends LinearLayout
@Override
public boolean handleCloseControls(boolean save, boolean force) {
- if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+ if (!save) {
// Undo changes and let the guts handle closing the view
mSelectedOption = null;
showSnoozeOptions(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 6ff711deeb01..01ee788f7fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -31,6 +31,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -42,6 +43,7 @@ import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyl
public class PromotedNotificationInfo extends NotificationInfo {
private static final String TAG = "PromotedNotifInfoGuts";
private INotificationManager mNotificationManager;
+ private PackageDemotionInteractor mPackageDemotionInteractor;
private NotificationGuts mGutsContainer;
public PromotedNotificationInfo(Context context, AttributeSet attrs) {
@@ -56,6 +58,7 @@ public class PromotedNotificationInfo extends NotificationInfo {
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -65,16 +68,19 @@ public class PromotedNotificationInfo extends NotificationInfo {
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
- onUserInteractionCallback, channelEditorDialogController, pkg, notificationChannel,
+ onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
+ pkg, notificationChannel,
entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
- isDeviceProvisioned, isNonblockable, wasShownHighPriority,
+ isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
assistantFeedbackController, metricsLogger, onCloseClick);
mNotificationManager = iNotificationManager;
+ mPackageDemotionInteractor = packageDemotionInteractor;
bindDemote(entry.getSbn(), pkg);
}
@@ -94,8 +100,8 @@ public class PromotedNotificationInfo extends NotificationInfo {
private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
return ((View v) -> {
try {
- // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+ mPackageDemotionInteractor.onPackageDemoted(packageName, sbn.getUid());
mGutsContainer.closeControls(v, true);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't revoke live update permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index 4082a5b35f1e..2c5b9f44bc58 100644
--- 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
@@ -61,7 +61,7 @@ constructor(
row: ExpandableNotificationRow,
context: Context,
): NotificationIconProvider {
- val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entry.sbn
+ val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entryLegacy.sbn
if (sbn == null) {
return object : NotificationIconProvider {
override fun shouldShowAppIcon(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index f492b259e58d..e266dad63d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -50,7 +50,7 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
resolveViews();
updateImageTag(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
}
private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index dec674c5a0f3..71bb9a2c9e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -47,7 +47,7 @@ public class NotificationBigTextTemplateViewWrapper extends NotificationTemplate
// the transformation types and we need to have our values set by then.
resolveViews(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
super.onContentUpdated(row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 585051ad26e1..e6dadcd7c8d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -225,7 +225,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
super.onContentUpdated(row);
mIsLowPriority = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().isAmbient()
- : row.getEntry().isAmbient();
+ : row.getEntryLegacy().isAmbient();
mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
@@ -236,7 +236,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
updateCropToPaddingForImageViews();
Notification n = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification()
- : row.getEntry().getSbn().getNotification();
+ : row.getEntryLegacy().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
// We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 99db1dba7e65..19321dcef5c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -327,7 +327,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
// the transformation types and we need to have our values set by then.
resolveTemplateViews(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
super.onContentUpdated(row);
// With the modern templates, a large icon visually overlaps the header, so we can't
// hide the header, we must show it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 64babb2449d7..35e286c18fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -83,7 +83,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
if (NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification().isStyle(
Notification.DecoratedCustomViewStyle.class)
- : row.getEntry().getSbn().getNotification().isStyle(
+ : row.getEntryLegacy().getSbn().getNotification().isStyle(
Notification.DecoratedCustomViewStyle.class)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
@@ -141,7 +141,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
// Apps targeting Q should fix their dark mode bugs.
int targetSdk = NotificationBundleUi.isEnabled()
? mRow.getEntryAdapter().getTargetSdk()
- : mRow.getEntry().targetSdk;
+ : mRow.getEntryLegacy().targetSdk;
if (targetSdk >= Build.VERSION_CODES.Q) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
index 9bd5a5bd903f..5a23f7cc2861 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -181,7 +181,7 @@ constructor(
it.setMagneticTranslation(targetTranslation)
}
}
- playPullHaptics(mappedTranslation = swipedRowMultiplier * translation, canSwipedBeDismissed)
+ // TODO(b/399633875): Enable pull haptics after we have a clear and polished haptics design
}
private fun playPullHaptics(mappedTranslation: Float, canSwipedBeDismissed: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 315d37e55bc3..f9d8c8e74b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -444,7 +444,7 @@ public class NotificationChildrenContainer extends ViewGroup
mIsConversation = isConversation;
StatusBarNotification notification = NotificationBundleUi.isEnabled()
? mContainingNotification.getEntryAdapter().getSbn()
- : mContainingNotification.getEntry().getSbn();
+ : mContainingNotification.getEntryLegacy().getSbn();
if (notification == null) {
return;
}
@@ -615,7 +615,7 @@ public class NotificationChildrenContainer extends ViewGroup
RemoteViews header;
StatusBarNotification notification = NotificationBundleUi.isEnabled()
? mContainingNotification.getEntryAdapter().getSbn()
- : mContainingNotification.getEntry().getSbn();
+ : mContainingNotification.getEntryLegacy().getSbn();
if (notification == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 96f0e6f57958..b5562ae15ede 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -167,7 +167,7 @@ internal constructor(
view === promoHeaderView -> BUCKET_PROMO
view is ExpandableNotificationRow ->
if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
- else view.entry.bucket
+ else view.entryLegacy.bucket
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e6bb1b9f0273..9fea75048e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -64,6 +64,7 @@ import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Pair;
import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -140,6 +141,7 @@ import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScr
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxy;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
@@ -1037,10 +1039,10 @@ public class NotificationStackScrollLayout
}
int bucket = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSectionBucket()
- : row.getEntry().getBucket();
+ : row.getEntryLegacy().getBucket();
boolean isAmbient = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().isAmbient()
- : row.getEntry().isAmbient();
+ : row.getEntryLegacy().isAmbient();
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
@@ -1845,7 +1847,7 @@ public class NotificationStackScrollLayout
} else {
if (row.isChildInGroup()) {
final NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(row.getEntry());
+ mGroupMembershipManager.getGroupSummary(row.getEntryLegacy());
if (groupSummary != null) {
row = groupSummary.getRow();
}
@@ -1998,16 +2000,16 @@ public class NotificationStackScrollLayout
if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow row) {
- NotificationEntry entry = row.getEntry();
boolean isEntrySummaryForTopHun;
if (NotificationBundleUi.isEnabled()) {
isEntrySummaryForTopHun = Objects.equals(
((ExpandableNotificationRow) slidingChild).getNotificationParent(),
mTopHeadsUpRow);
} else {
+ NotificationEntry entry = row.getEntryLegacy();
isEntrySummaryForTopHun = mTopHeadsUpRow != null &&
- mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
- == entry;
+ mGroupMembershipManager.getGroupSummary(
+ mTopHeadsUpRow.getEntryLegacy()) == entry;
}
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mTopHeadsUpRow != row
@@ -3007,7 +3009,7 @@ public class NotificationStackScrollLayout
ExpandableNotificationRow childRow = (ExpandableNotificationRow) child;
return NotificationBundleUi.isEnabled()
? mGroupMembershipManager.isChildInGroup(childRow.getEntryAdapter())
- : mGroupMembershipManager.isChildInGroup(childRow.getEntry());
+ : mGroupMembershipManager.isChildInGroup(childRow.getEntryLegacy());
}
return false;
}
@@ -3852,8 +3854,6 @@ public class NotificationStackScrollLayout
// existing overScroll, we have to scroll the view
customOverScrollBy((int) scrollAmount, getOwnScrollY(),
range, getHeight() / 2);
- // If we're scrolling, leavebehinds should be dismissed
- mController.checkSnoozeLeavebehind();
}
}
break;
@@ -6002,7 +6002,6 @@ public class NotificationStackScrollLayout
* LockscreenShadeTransitionController resets fraction to 0
* where it remains until the next lockscreen-to-shade transition.
*/
- @Override
public void setFractionToShade(float fraction) {
mAmbientState.setFractionToShade(fraction);
updateContentHeight(); // Recompute stack height with different section gap.
@@ -6474,7 +6473,7 @@ public class NotificationStackScrollLayout
@SelectedRows int selection) {
int bucket = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSectionBucket()
- : row.getEntry().getBucket();
+ : row.getEntryLegacy().getBucket();
switch (selection) {
case ROWS_ALL:
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7f8acf5fda9..f3d8ee245540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -648,11 +648,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public void onChildSnappedBack(View animView, float targetLeft) {
mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow row) {
- if (row.isPinned() && !canChildBeDismissed(row)
- && NotificationBundleUi.isEnabled()
+ boolean cannotFullScreen = NotificationBundleUi.isEnabled()
? !row.getEntryAdapter().isFullScreenCapable()
- : (row.getEntry().getSbn().getNotification().fullScreenIntent
- == null)) {
+ : (row.getEntryLegacy().getSbn().getNotification().fullScreenIntent
+ == null);
+ if (row.isPinned() && !canChildBeDismissed(row) && cannotFullScreen) {
mHeadsUpManager.removeNotification(
row.getKey(),
/* removeImmediately= */ true,
@@ -1735,7 +1735,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
* they remain until the next lockscreen-to-shade transition.
*/
public void setTransitionToFullShadeAmount(float fraction) {
- SceneContainerFlag.assertInLegacyMode();
mView.setFractionToShade(fraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index fcb63df1a528..e5071d9c1e53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -413,7 +413,7 @@ constructor(
(currentNotification as? ExpandableNotificationRow)?.entryAdapter
counter.incrementForBucket(entryAdapter?.sectionBucket)
} else {
- val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+ val entry = (currentNotification as? ExpandableNotificationRow)?.entryLegacy
counter.incrementForBucket(entry?.bucket)
}
}
@@ -470,7 +470,7 @@ constructor(
calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
val canPeek = view is ExpandableNotificationRow &&
if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
- else view.entry.isStickyAndNotDemoted
+ else view.entryLegacy.isStickyAndNotDemoted
var size =
if (onLockscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 28218227506c..da1442359071 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -927,7 +927,7 @@ public class StackScrollAlgorithm {
childState.headsUpIsVisible, row.showingPulsing(),
ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
? row.getEntryAdapter().canPeek()
- : row.getEntry().isStickyAndNotDemoted())) {
+ : row.getEntryLegacy().isStickyAndNotDemoted())) {
// the height of this child before clamping it to the top
float unmodifiedChildHeight = childState.height;
clampHunToTop(
@@ -984,7 +984,7 @@ public class StackScrollAlgorithm {
childState.headsUpIsVisible, row.showingPulsing(),
ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
? row.getEntryAdapter().canPeek()
- : row.getEntry().isStickyAndNotDemoted())) {
+ : row.getEntryLegacy().isStickyAndNotDemoted())) {
// Ensure that the heads up is always visible even when scrolled off.
// NSSL y starts at top of screen in non-split-shade, but below the qs
// offset
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index ac89f3a63fcd..9c855e9cd9b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -107,9 +107,6 @@ interface NotificationScrollView {
/** sets the current expand fraction */
fun setExpandFraction(expandFraction: Float)
- /** Sets the fraction of the LockScreen -> Shade transition. */
- fun setFractionToShade(fraction: Float)
-
/** sets the current QS expand fraction */
fun setQsExpandFraction(expandFraction: Float)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 40739b386d20..653344ae9203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -95,11 +95,6 @@ constructor(
view.setExpandFraction(it.coerceIn(0f, 1f))
}
}
- launch {
- viewModel.lockScreenToShadeTransitionProgress.collectTraced {
- view.setFractionToShade(it.coerceIn(0f, 1f))
- }
- }
launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 940b2e541758..c1aa5f12aa99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -207,44 +207,6 @@ constructor(
val qsExpandFraction: Flow<Float> =
shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
- /**
- * Fraction of the LockScreen -> Shade transition. 0..1 while the transition in progress, and
- * snaps back to 0 when it is Idle.
- */
- val lockScreenToShadeTransitionProgress: Flow<Float> =
- combine(
- shadeInteractor.shadeExpansion,
- shadeModeInteractor.shadeMode,
- sceneInteractor.transitionState,
- ) { shadeExpansion, _, transitionState ->
- when (transitionState) {
- is Idle -> 0f
- is ChangeScene ->
- if (
- transitionState.isTransitioning(
- from = Scenes.Lockscreen,
- to = Scenes.Shade,
- )
- ) {
- shadeExpansion
- } else {
- 0f
- }
-
- is Transition.OverlayTransition ->
- if (
- transitionState.currentScene == Scenes.Lockscreen &&
- transitionState.isTransitioning(to = Overlays.NotificationsShade)
- ) {
- shadeExpansion
- } else {
- 0f
- }
- }
- }
- .distinctUntilChanged()
- .dumpWhileCollecting("lockScreenToShadeTransitionProgress")
-
val isOccluded: Flow<Boolean> =
bouncerInteractor.bouncerExpansion
.map { it == 1f }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index bc533148f514..afe79718d526 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -60,7 +60,7 @@ constructor(
if (animationsEnabled) {
added.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+ val hasStatusBarChip = statusBarChips.contains(row.key)
parentView.generateHeadsUpAnimation(
row,
/* isHeadsUp = */ true,
@@ -69,7 +69,7 @@ constructor(
}
removed.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+ val hasStatusBarChip = statusBarChips.contains(row.key)
if (!parentView.isBeingDragged()) {
parentView.generateHeadsUpAnimation(
row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74a42ef3ff7d..f3d72027238f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.util.Log;
@@ -226,6 +227,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private ScrimState mState = ScrimState.UNINITIALIZED;
+ private Context mContext;
+
private ScrimView mScrimInFront;
private ScrimView mNotificationsScrim;
private ScrimView mScrimBehind;
@@ -365,7 +368,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
BlurConfig blurConfig,
+ @Main Context context,
Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) {
+ mContext = context;
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mBlurConfig = blurConfig;
@@ -1627,16 +1632,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void updateThemeColors() {
if (mScrimBehind == null) return;
- int background = mScrimBehind.getContext().getColor(
+ int background = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceDim);
- int accent = mScrimBehind.getContext().getColor(
+ int accent = mContext.getColor(
com.android.internal.R.color.materialColorPrimary);
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
mColors.setSupportsDarkText(isBackgroundLight);
- int surface = mScrimBehind.getContext().getColor(
+ int surface = mContext.getColor(
com.android.internal.R.color.materialColorSurface);
for (ScrimState state : ScrimState.values()) {
state.setSurfaceColor(surface);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 05a46cd9fa31..8389aab4aac8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -219,7 +219,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
if (NotificationBundleUi.isEnabled()) {
mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
} else {
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
}
} else if (!row.isChildInGroup()) {
final boolean expandNotification;
@@ -241,7 +241,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
if (NotificationBundleUi.isEnabled()) {
mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
} else {
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index c91ea9a50028..2f9cff46d687 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -196,9 +196,8 @@ fun StatusBarRoot(
setContent {
PlatformTheme {
- val chipsVisibilityModel by
+ val chipsVisibilityModel =
statusBarViewModel.ongoingActivityChips
- .collectAsStateWithLifecycle()
if (chipsVisibilityModel.areChipsAllowed) {
OngoingActivityChips(
chips = chipsVisibilityModel.chips,
@@ -255,7 +254,7 @@ fun StatusBarRoot(
expandedMatchesParentHeight = true
showsOnlyActiveMedia = true
falsingProtectionNeeded = false
- disablePagination = true
+ disableScrolling = true
init(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index c717b180575c..540babad5dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -21,6 +21,8 @@ import android.graphics.Rect
import android.view.Display
import android.view.View
import androidx.compose.runtime.getValue
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TrackGroupUtils.trackGroup
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -130,7 +132,7 @@ interface HomeStatusBarViewModel : Activatable {
val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
/** All supported activity chips, whether they are currently active or not. */
- val ongoingActivityChips: StateFlow<ChipsVisibilityModel>
+ val ongoingActivityChips: ChipsVisibilityModel
/**
* The multiple ongoing activity chips that should be shown on the left-hand side of the status
@@ -386,11 +388,9 @@ constructor(
}
override val isHomeStatusBarAllowed =
- isHomeStatusBarAllowedCompat.stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
+ isHomeStatusBarAllowedCompat
+ .traceEach(trackGroup(TRACK_GROUP, "isHomeStatusBarAllowed"), logcat = true)
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), initialValue = false)
private val shouldHomeStatusBarBeVisible =
combine(
@@ -461,24 +461,29 @@ constructor(
isHomeStatusBarAllowed && !isSecureCameraActive && !hideStartSideContentForHeadsUp
}
- override val ongoingActivityChips =
+ private val chipsVisibilityModel: Flow<ChipsVisibilityModel> =
combine(ongoingActivityChipsViewModel.chips, canShowOngoingActivityChips) { chips, canShow
->
ChipsVisibilityModel(chips, areChipsAllowed = canShow)
}
- .stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue =
- ChipsVisibilityModel(
- chips = MultipleOngoingActivityChipsModel(),
- areChipsAllowed = false,
- ),
- )
+ .traceEach(trackGroup(TRACK_GROUP, "chips"), logcat = true) {
+ "Chips[allowed=${it.areChipsAllowed} numChips=${it.chips.active.size}]"
+ }
+
+ override val ongoingActivityChips: ChipsVisibilityModel by
+ hydrator.hydratedStateOf(
+ traceName = "ongoingActivityChips",
+ initialValue =
+ ChipsVisibilityModel(
+ chips = MultipleOngoingActivityChipsModel(),
+ areChipsAllowed = false,
+ ),
+ source = chipsVisibilityModel,
+ )
private val hasOngoingActivityChips =
if (StatusBarChipsModernization.isEnabled) {
- ongoingActivityChips.map { it.chips.active.any { chip -> !chip.isHidden } }
+ chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
} else if (StatusBarNotifChips.isEnabled) {
ongoingActivityChipsLegacy.map { it.primary is OngoingActivityChipModel.Active }
} else {
@@ -607,6 +612,8 @@ constructor(
private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
+ private const val TRACK_GROUP = "StatusBar"
+
fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01be40f7..fa022b4768fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import androidx.annotation.NonNull;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@ public final class DeviceStateRotationLockSettingController
private int mDeviceState = -1;
@Nullable
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
- private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
- mDeviceStateRotationLockSettingsListener;
+ private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+ mDeviceStateAutoRotateSettingListener;
@Inject
public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@ public final class DeviceStateRotationLockSettingController
// is no user action.
mDeviceStateCallback = this::updateDeviceState;
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
- mDeviceStateRotationLockSettingsListener = () ->
+ mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
mDeviceStateRotationLockSettingsManager.registerListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
- if (mDeviceStateRotationLockSettingsListener != null) {
+ if (mDeviceStateAutoRotateSettingListener != null) {
mDeviceStateRotationLockSettingsManager.unregisterListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9f60fe212567..bd3feadf4459 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -28,6 +28,7 @@ import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_BOTH;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
@@ -106,6 +107,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -507,18 +509,52 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
+ // Condition only applies when booting to Setup Wizard.
+ // We should check if the device has specific hardware color styles, load the relative
+ // color palette and more also save the setting in our shared setting with ThemePicker.
WallpaperColors systemColor;
if (hardwareColorStyles() && !mDeviceProvisionedController.isCurrentUserSetup()) {
- Pair<Integer, Color> defaultSettings = getThemeSettingsDefaults();
- mThemeStyle = defaultSettings.first;
- Color seedColor = defaultSettings.second;
+ HardwareDefaultSetting defaultSettings = getThemeSettingsDefaults();
+ mThemeStyle = defaultSettings.style;
// we only use the first color anyway, so we can pass only the single color we have
systemColor = new WallpaperColors(
- /*primaryColor*/ seedColor,
- /*secondaryColor*/ seedColor,
- /*tertiaryColor*/ seedColor
+ /*primaryColor*/ defaultSettings.seedColor,
+ /*secondaryColor*/ defaultSettings.seedColor,
+ /*tertiaryColor*/ defaultSettings.seedColor
+ );
+
+ /* We update the json in THEME_CUSTOMIZATION_OVERLAY_PACKAGES to reflect the preset. */
+ final int currentUser = mUserTracker.getUserId();
+ final String overlayPackageJson = Objects.requireNonNullElse(
+ mSecureSettings.getStringForUser(
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ currentUser),
+ "{}"
);
+
+ try {
+ JSONObject object = new JSONObject(overlayPackageJson);
+ String seedColorStr = Integer.toHexString(defaultSettings.seedColor.toArgb());
+ object.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, seedColorStr);
+ object.put(OVERLAY_CATEGORY_ACCENT_COLOR, seedColorStr);
+ object.put(OVERLAY_COLOR_SOURCE, defaultSettings.colorSource);
+ object.put(OVERLAY_CATEGORY_THEME_STYLE, Style.toString(mThemeStyle));
+
+ mSecureSettings.putStringForUser(
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, object.toString(),
+ UserHandle.USER_CURRENT);
+
+ Log.d(TAG, "Hardware Color Defaults loaded: " + object.toString());
+
+ } catch (JSONException e) {
+ Log.d(TAG, "Failed to store hardware color defaults in "
+ + "THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+ }
+
+ // now we have to update
+
} else {
systemColor = mWallpaperManager.getWallpaperColors(
getDefaultWallpaperColorsSource(mUserTracker.getUserId()));
@@ -863,7 +899,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
try {
JSONObject object = new JSONObject(overlayPackageJson);
style = Style.valueOf(
- object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
+ object.getString(OVERLAY_CATEGORY_THEME_STYLE));
if (!validStyles.contains(style)) {
style = Style.TONAL_SPOT;
}
@@ -899,26 +935,29 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
}
String deviceColorPropertyValue = mSystemPropertiesHelper.get(deviceColorProperty);
- Pair<Integer, String> selectedTheme = themeMap.get(deviceColorPropertyValue);
- if (selectedTheme == null) {
+ Pair<Integer, String> styleAndSource = themeMap.get(deviceColorPropertyValue);
+ if (styleAndSource == null) {
Log.d(TAG, "Sysprop `" + deviceColorProperty + "` of value '" + deviceColorPropertyValue
+ "' not found in theming_defaults: " + Arrays.toString(themeData));
- selectedTheme = fallbackTheme;
+ styleAndSource = fallbackTheme;
}
- return selectedTheme;
+ return styleAndSource;
+ }
+
+ record HardwareDefaultSetting(Color seedColor, @Style.Type int style, String colorSource) {
}
@VisibleForTesting
- protected Pair<Integer, Color> getThemeSettingsDefaults() {
+ protected HardwareDefaultSetting getThemeSettingsDefaults() {
- Pair<Integer, String> selectedTheme = getHardwareColorSetting();
+ Pair<Integer, String> styleAndSource = getHardwareColorSetting();
// Last fallback color
Color defaultSeedColor = Color.valueOf(GOOGLE_BLUE);
// defaultColor will come from wallpaper or be parsed from a string
- boolean isWallpaper = selectedTheme.second.equals(COLOR_SOURCE_HOME);
+ boolean isWallpaper = styleAndSource.second.equals(COLOR_SOURCE_HOME);
if (isWallpaper) {
WallpaperColors wallpaperColors = mWallpaperManager.getWallpaperColors(
@@ -932,22 +971,24 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
defaultSeedColor.toArgb()));
} else {
try {
- defaultSeedColor = Color.valueOf(Color.parseColor(selectedTheme.second));
+ defaultSeedColor = Color.valueOf(Color.parseColor(styleAndSource.second));
Log.d(TAG, "Default seed color read from resource: " + Integer.toHexString(
defaultSeedColor.toArgb()));
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Error parsing color: " + selectedTheme.second, e);
+ Log.e(TAG, "Error parsing color: " + styleAndSource.second, e);
// defaultSeedColor remains unchanged in this case
}
}
- return new Pair<>(selectedTheme.first, defaultSeedColor);
+ return new HardwareDefaultSetting(defaultSeedColor, styleAndSource.first,
+ isWallpaper ? COLOR_SOURCE_HOME : COLOR_SOURCE_PRESET);
}
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mSystemColors=" + mCurrentColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
+ pw.println("mContrast=" + mContrast);
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mDynamicOverlay=" + mDynamicOverlay);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
index 47e27bc59f96..1cc7a3185a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -50,6 +50,7 @@ class DisplaySwitchLatencyLogger {
onScreenTurningOnToOnDrawnMs,
onDrawnToOnScreenTurnedOnMs,
trackingResult,
+ screenWakelockstatus
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 66de52260b79..5800d5ed41c6 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -344,6 +344,8 @@ constructor(
val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN,
val trackingResult: Int =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT,
+ val screenWakelockstatus: Int =
+ SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN,
)
enum class TrackingResult {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 735da46667c5..cc4307a67268 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -19,8 +19,11 @@ package com.android.systemui.util.kotlin
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.SystemClockImpl
+import java.util.LinkedList
import java.util.concurrent.atomic.AtomicReference
import kotlin.math.max
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -364,3 +367,58 @@ inline fun <T1, T2, T3, T4, T5, T6, T7, T8, T9, R> combine(
*/
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<Unit>.emitOnStart(): Flow<Unit> = onStart { emit(Unit) }
+
+/**
+ * Transforms a Flow<T> into a Flow<List<T>> by implementing a sliding window algorithm.
+ *
+ * This function creates a sliding window over the input Flow<T>. The window has a specified
+ * [windowDuration] and slides continuously as time progresses. The emitted List<T> contains all
+ * items from the input flow that fall within the current window.
+ *
+ * The window slides forward by the smallest possible increment to include or exclude *one* event
+ * based on the time the event was emitted (determined by the System.currentTimeMillis()). This
+ * means that consecutive emitted lists will have overlapping elements if the elements fall within
+ * the [windowDuration]
+ *
+ * @param windowDuration The duration of the sliding window.
+ * @return A Flow that emits Lists of elements within the current sliding window.
+ */
+fun <T> Flow<T>.slidingWindow(
+ windowDuration: Duration,
+ clock: SystemClock = SystemClockImpl(),
+): Flow<List<T>> = channelFlow {
+ require(windowDuration.isPositive()) { "Window duration must be positive" }
+ val buffer = LinkedList<Pair<Duration, T>>()
+
+ coroutineScope {
+ var windowAdvancementJob: Job? = null
+
+ collect { value ->
+ windowAdvancementJob?.cancel()
+ val now = clock.currentTimeMillis().milliseconds
+ buffer.addLast(now to value)
+
+ while (buffer.isNotEmpty() && buffer.first.first + windowDuration <= now) {
+ buffer.removeFirst()
+ }
+ send(buffer.map { it.second })
+
+ // Keep the window advancing through time even if the source flow isn't emitting
+ // anymore. We stop advancing the window as soon as there are no items left in the
+ // buffer.
+ windowAdvancementJob = launch {
+ while (buffer.isNotEmpty()) {
+ val startOfWindow = clock.currentTimeMillis().milliseconds - windowDuration
+ // Invariant: At this point, everything in the buffer is guaranteed to be in
+ // the window, as we removed expired items above.
+ val timeUntilNextOldest =
+ (buffer.first.first - startOfWindow).coerceAtLeast(0.milliseconds)
+ delay(timeUntilNextOldest)
+ // Remove the oldest item, as it has now fallen out of the window.
+ buffer.removeFirst()
+ send(buffer.map { it.second })
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index a42f5d3f67ea..9475bdbd043b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -50,7 +50,6 @@ import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -1878,7 +1877,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(callback, never()).onTrustGrantedForCurrentUser(
anyBoolean() /* dismissKeyguard */,
eq(true) /* newlyUnlocked */,
- anyObject() /* flags */,
+ any() /* flags */,
anyString() /* message */
);
}
@@ -2747,7 +2746,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
() -> mJavaAdapter,
() -> mSceneInteractor,
() -> mCommunalSceneInteractor,
- mKeyguardServiceShowLockscreenInteractor);
+ () -> mKeyguardServiceShowLockscreenInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
index 4f7610ab7d72..f98c1309b24f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
@@ -25,7 +25,6 @@ import com.android.systemui.dagger.GlobalRootComponent
import com.android.systemui.dagger.SysUIComponent
import com.android.systemui.dump.dumpManager
import com.android.systemui.flags.systemPropertiesHelper
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.process.processWrapper
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -48,7 +47,7 @@ class SystemUIApplicationTest : SysuiTestCase() {
@get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
@Mock private lateinit var initializer: SystemUIInitializer
@Mock private lateinit var rootComponent: GlobalRootComponent
@Mock private lateinit var sysuiComponent: SysUIComponent
@@ -56,9 +55,13 @@ class SystemUIApplicationTest : SysuiTestCase() {
@Mock private lateinit var initController: InitController
class StartableA : TestableStartable()
+
class StartableB : TestableStartable()
+
class StartableC : TestableStartable()
+
class StartableD : TestableStartable()
+
class StartableE : TestableStartable()
val dependencyMap: Map<Class<*>, Set<Class<out CoreStartable>>> =
@@ -114,7 +117,7 @@ class SystemUIApplicationTest : SysuiTestCase() {
.thenReturn(
mutableMapOf(
StartableA::class.java to Provider { startableA },
- StartableB::class.java to Provider { startableB }
+ StartableB::class.java to Provider { startableB },
)
)
app.onCreate()
@@ -130,7 +133,7 @@ class SystemUIApplicationTest : SysuiTestCase() {
mutableMapOf(
StartableC::class.java to Provider { startableC },
StartableA::class.java to Provider { startableA },
- StartableB::class.java to Provider { startableB }
+ StartableB::class.java to Provider { startableB },
)
)
app.onCreate()
@@ -150,7 +153,7 @@ class SystemUIApplicationTest : SysuiTestCase() {
StartableC::class.java to Provider { startableC },
StartableD::class.java to Provider { startableD },
StartableA::class.java to Provider { startableA },
- StartableB::class.java to Provider { startableB }
+ StartableB::class.java to Provider { startableB },
)
)
app.onCreate()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 1268de0f4141..4cfe106780f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -34,9 +34,9 @@ import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.runOnMainThreadAndWaitForIdleSync
+import com.android.systemui.testKosmos
import kotlin.test.assertTrue
import org.junit.Rule
import org.junit.Test
@@ -77,7 +77,7 @@ class TransitionAnimatorTest(
}
}
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val pathManager =
GoldenPathManager(
context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 0e68fce679b0..2c70249bcb06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -73,9 +73,9 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.withArgCaptor
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
@@ -189,7 +189,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
private lateinit var promptContentViewWithMoreOptionsButton:
PromptContentViewWithMoreOptionsButton
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
@Before
fun setup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
index 9dab9d735603..b134dff89651 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
@@ -31,8 +31,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.motion.createSysUiComposeMotionTestRule
+import com.android.systemui.testKosmos
import com.android.systemui.utils.PolicyRestriction
import kotlin.test.Test
import kotlin.time.Duration.Companion.seconds
@@ -61,7 +61,7 @@ import platform.test.screenshot.Displays.Phone
class BrightnessSliderMotionTest : SysuiTestCase() {
private val deviceSpec = DeviceEmulationSpec(Phone)
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
@get:Rule val motionTestRule = createSysUiComposeMotionTestRule(kosmos, deviceSpec)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 5c893da45b8d..6a4f3da054e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -44,6 +44,7 @@ import android.app.RemoteAction;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
@@ -77,6 +78,7 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Optional;
+import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -158,6 +160,31 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
* is false are removed.[
*/
private void initController() {
+ IntentCreator fakeIntentCreator = new IntentCreator() {
+ @Override
+ public Intent getTextEditorIntent(Context context) {
+ return new Intent();
+ }
+
+ @Override
+ public Intent getShareIntent(ClipData clipData, Context context) {
+ Intent intent = Intent.createChooser(new Intent(Intent.ACTION_SEND), null);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ @Override
+ public void getImageEditIntentAsync(Uri uri, Context context,
+ Consumer<Intent> outputConsumer) {
+ outputConsumer.accept(new Intent(Intent.ACTION_EDIT));
+ }
+
+ @Override
+ public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ return new Intent();
+ }
+ };
+
mOverlayController = new ClipboardOverlayController(
mContext,
mClipboardOverlayView,
@@ -171,7 +198,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mClipboardTransitionExecutor,
mClipboardIndicationProvider,
mUiEventLogger,
- new ActionIntentCreator());
+ fakeIntentCreator);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
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 fb70846049da..061f7984d44b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -134,7 +134,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private PackageManager mPackageManager;
- @Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
@Mock private ShadeController mShadeController;
@@ -148,6 +147,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private GlobalActionsInteractor mInteractor;
+ private Handler mHandler;
@Before
public void setUp() throws Exception {
@@ -166,6 +166,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
mInteractor = mKosmos.getGlobalActionsInteractor();
+ mHandler = new Handler(mTestableLooper.getLooper());
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -771,6 +772,31 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
}
+ @Test
+ public void userSwitching_dismissDialog() {
+ String[] actions = {
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mResources)
+ .getStringArray(com.android.internal.R.array.config_globalActionsList);
+
+ mGlobalActionsDialogLite.showOrHideDialog(false, true, null, Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog.isShowing()).isTrue();
+
+ ArgumentCaptor<UserTracker.Callback> captor =
+ ArgumentCaptor.forClass(UserTracker.Callback.class);
+
+ verify(mUserTracker).addCallback(captor.capture(), any());
+
+ captor.getValue().onBeforeUserSwitching(100);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog).isNull();
+ }
+
private UserInfo mockCurrentUser(int flags) {
return new UserInfo(10, "A User", flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index f394c805f5b7..8f1d07bac4df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -855,6 +855,20 @@ public class SeekBarViewModelTest : SysuiTestCase() {
@Test
fun contentDescriptionUpdated() {
+ var elapsedTimeDesc: CharSequence? = null
+ var durationDesc: CharSequence? = null
+ val listener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ elapsedTimeDesc = elapsedTimeDescription
+ durationDesc = durationDescription
+ }
+ }
+ viewModel.setContentDescriptionListener(listener)
+
// When there is a duration and position
val duration = (1.5 * 60 * 60 * 1000).toLong()
val metadata =
@@ -875,9 +889,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
viewModel.updateController(mockController)
fakeExecutor.runNextReady()
- // Then the content description is set
- val result = viewModel.progress.value!!
-
+ // Then the content description listener gets an update
val expectedProgress =
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
.formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
Measure(30, MeasureUnit.MINUTE),
Measure(0, MeasureUnit.SECOND),
)
- assertThat(result.durationDescription).isEqualTo(expectedDuration)
- assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+ assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
+ assertThat(durationDesc).isEqualTo(expectedDuration)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 5c26dac5eb30..798aa428e73e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -60,13 +60,13 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
import android.text.TextUtils;
import android.view.View;
import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.media.flags.Flags;
@@ -101,6 +101,9 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -108,7 +111,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MediaSwitchingControllerTest extends SysuiTestCase {
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
@@ -201,6 +204,17 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
private MediaDescription mMediaDescription;
private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION,
+ Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING);
+ }
+
+ public MediaSwitchingControllerTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() {
mPackageName = mContext.getPackageName();
@@ -260,7 +274,6 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
mMediaDevices.add(mMediaDevice1);
mMediaDevices.add(mMediaDevice2);
-
when(mNearbyDevice1.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID);
when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_FAR);
when(mNearbyDevice2.getMediaRoute2Id()).thenReturn(TEST_DEVICE_2_ID);
@@ -689,7 +702,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
mMediaSwitchingController.start(mCb);
reset(mCb);
- mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.clearMediaItemList();
mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
final List<MediaDevice> devices = new ArrayList<>();
int dividerSize = 0;
@@ -1528,7 +1541,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
.getSelectedMediaDevice();
mMediaSwitchingController.start(mCb);
reset(mCb);
- mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.clearMediaItemList();
mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
@@ -1546,7 +1559,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
.getSelectedMediaDevice();
mMediaSwitchingController.start(mCb);
reset(mCb);
- mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.clearMediaItemList();
mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
@@ -1564,7 +1577,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
.getSelectedMediaDevice();
mMediaSwitchingController.start(mCb);
reset(mCb);
- mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.clearMediaItemList();
mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
@@ -1582,7 +1595,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
.getSelectedMediaDevice();
mMediaSwitchingController.start(mCb);
reset(mCb);
- mMediaSwitchingController.getMediaItemList().clear();
+ mMediaSwitchingController.clearMediaItemList();
mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
mMediaDevices.clear();
mMediaDevices.add(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java
new file mode 100644
index 000000000000..f6edd49f142f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.media.flags.Flags;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4.class)
+@TestableLooper.RunWithLooper
+public class OutputMediaItemListProxyTest extends SysuiTestCase {
+ private static final String DEVICE_ID_1 = "device_id_1";
+ private static final String DEVICE_ID_2 = "device_id_2";
+ private static final String DEVICE_ID_3 = "device_id_3";
+ private static final String DEVICE_ID_4 = "device_id_4";
+ @Mock private MediaDevice mMediaDevice1;
+ @Mock private MediaDevice mMediaDevice2;
+ @Mock private MediaDevice mMediaDevice3;
+ @Mock private MediaDevice mMediaDevice4;
+
+ private MediaItem mMediaItem1;
+ private MediaItem mMediaItem2;
+ private MediaItem mConnectNewDeviceMediaItem;
+ private OutputMediaItemListProxy mOutputMediaItemListProxy;
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION,
+ Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING);
+ }
+
+ public OutputMediaItemListProxyTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mMediaDevice1.getId()).thenReturn(DEVICE_ID_1);
+ when(mMediaDevice2.getId()).thenReturn(DEVICE_ID_2);
+ when(mMediaDevice2.isSuggestedDevice()).thenReturn(true);
+ when(mMediaDevice3.getId()).thenReturn(DEVICE_ID_3);
+ when(mMediaDevice4.getId()).thenReturn(DEVICE_ID_4);
+ mMediaItem1 = MediaItem.createDeviceMediaItem(mMediaDevice1);
+ mMediaItem2 = MediaItem.createDeviceMediaItem(mMediaDevice2);
+ mConnectNewDeviceMediaItem = MediaItem.createPairNewDeviceMediaItem();
+
+ mOutputMediaItemListProxy = new OutputMediaItemListProxy(mContext);
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void updateMediaDevices_shouldUpdateMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ // Create the initial output media item list with mMediaDevice2 and mMediaDevice3.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+ /* selectedDevices */ List.of(mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ // Check the output media items to be
+ // * a media item with the selected mMediaDevice3
+ // * a group divider for suggested devices
+ // * a media item with the mMediaDevice2
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice3, null, mMediaDevice2);
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+ .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+
+ // Update the output media item list with more media devices.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice4, mMediaDevice1, mMediaDevice3, mMediaDevice2),
+ /* selectedDevices */ List.of(mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ // Check the output media items to be
+ // * a media item with the selected route mMediaDevice3
+ // * a group divider for suggested devices
+ // * a media item with the route mMediaDevice2
+ // * a group divider for speakers and displays
+ // * a media item with the route mMediaDevice4
+ // * a media item with the route mMediaDevice1
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(
+ mMediaDevice3, null, mMediaDevice2, null, mMediaDevice4, mMediaDevice1);
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+ .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+
+ // Update the output media item list where mMediaDevice4 is offline and new selected device.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice1, mMediaDevice3, mMediaDevice2),
+ /* selectedDevices */ List.of(mMediaDevice1, mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ // Check the output media items to be
+ // * a media item with the selected route mMediaDevice3
+ // * a group divider for suggested devices
+ // * a media item with the route mMediaDevice2
+ // * a group divider for speakers and displays
+ // * a media item with the route mMediaDevice1
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice3, null, mMediaDevice2, null, mMediaDevice1);
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+ .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void updateMediaDevices_multipleSelectedDevices_shouldHaveCorrectDeviceOrdering() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ // Create the initial output media item list with mMediaDevice2 and mMediaDevice3.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice2, mMediaDevice4, mMediaDevice3, mMediaDevice1),
+ /* selectedDevices */ List.of(mMediaDevice1, mMediaDevice2, mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ if (Flags.enableOutputSwitcherDeviceGrouping()) {
+ // When the device grouping is enabled, the order of selected devices are preserved:
+ // * a media item with the selected mMediaDevice2
+ // * a media item with the selected mMediaDevice3
+ // * a media item with the selected mMediaDevice1
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(
+ mMediaDevice2, mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+ assertThat(
+ mOutputMediaItemListProxy
+ .getOutputMediaItemList()
+ .get(0)
+ .isFirstDeviceInGroup())
+ .isTrue();
+ } else {
+ // When the device grouping is disabled, the order of selected devices are reverted:
+ // * a media item with the selected mMediaDevice1
+ // * a media item with the selected mMediaDevice3
+ // * a media item with the selected mMediaDevice2
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(
+ mMediaDevice1, mMediaDevice3, mMediaDevice2, null, mMediaDevice4);
+ }
+
+ // Update the output media item list with a selected device being deselected.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice4, mMediaDevice1, mMediaDevice3, mMediaDevice2),
+ /* selectedDevices */ List.of(mMediaDevice2, mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ if (Flags.enableOutputSwitcherDeviceGrouping()) {
+ // When the device grouping is enabled, the order of selected devices are preserved:
+ // * a media item with the selected mMediaDevice2
+ // * a media item with the selected mMediaDevice3
+ // * a media item with the selected mMediaDevice1
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(
+ mMediaDevice2, mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+ assertThat(
+ mOutputMediaItemListProxy
+ .getOutputMediaItemList()
+ .get(0)
+ .isFirstDeviceInGroup())
+ .isTrue();
+ } else {
+ // When the device grouping is disabled, the order of selected devices are reverted:
+ // * a media item with the selected mMediaDevice1
+ // * a media item with the selected mMediaDevice3
+ // * a media item with the selected mMediaDevice2
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(
+ mMediaDevice1, mMediaDevice3, mMediaDevice2, null, mMediaDevice4);
+ }
+
+ // Update the output media item list with a selected device is missing.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice1, mMediaDevice3, mMediaDevice4),
+ /* selectedDevices */ List.of(mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ if (Flags.enableOutputSwitcherDeviceGrouping()) {
+ // When the device grouping is enabled, the order of selected devices are preserved:
+ // * a media item with the selected mMediaDevice3
+ // * a media item with the selected mMediaDevice1
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+ assertThat(
+ mOutputMediaItemListProxy
+ .getOutputMediaItemList()
+ .get(0)
+ .isFirstDeviceInGroup())
+ .isTrue();
+ } else {
+ // When the device grouping is disabled, the order of selected devices are reverted:
+ // * a media item with the selected mMediaDevice1
+ // * a media item with the selected mMediaDevice3
+ // * a group divider for speakers and displays
+ // * a media item with the mMediaDevice4
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice1, mMediaDevice3, null, mMediaDevice4);
+ }
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void updateMediaDevices_withConnectNewDeviceMediaItem_shouldUpdateMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ // Create the initial output media item list with a connect new device media item.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+ /* selectedDevices */ List.of(mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ mConnectNewDeviceMediaItem);
+
+ // Check the output media items to be
+ // * a media item with the selected mMediaDevice3
+ // * a group divider for suggested devices
+ // * a media item with the mMediaDevice2
+ // * a connect new device media item
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList())
+ .contains(mConnectNewDeviceMediaItem);
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice3, null, mMediaDevice2, null);
+
+ // Update the output media item list without a connect new device media item.
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+ /* selectedDevices */ List.of(mMediaDevice3),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+
+ // Check the output media items to be
+ // * a media item with the selected mMediaDevice3
+ // * a group divider for suggested devices
+ // * a media item with the mMediaDevice2
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList())
+ .doesNotContain(mConnectNewDeviceMediaItem);
+ assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+ .containsExactly(mMediaDevice3, null, mMediaDevice2);
+ }
+
+ @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void clearAndAddAll_shouldUpdateMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem1);
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+ mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem2));
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem2);
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void clear_flagOn_shouldClearMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice1),
+ /* selectedDevices */ List.of(),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+ mOutputMediaItemListProxy.clear();
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+ }
+
+ @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void clear_flagOff_shouldClearMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+ mOutputMediaItemListProxy.clear();
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void removeMutingExpectedDevices_flagOn_shouldClearMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ mOutputMediaItemListProxy.updateMediaDevices(
+ /* devices= */ List.of(mMediaDevice1),
+ /* selectedDevices */ List.of(),
+ /* connectedMediaDevice= */ null,
+ /* needToHandleMutingExpectedDevice= */ false,
+ /* connectNewDeviceMediaItem= */ null);
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+ mOutputMediaItemListProxy.removeMutingExpectedDevices();
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+ }
+
+ @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+ @Test
+ public void removeMutingExpectedDevices_flagOff_shouldClearMediaItemList() {
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+ mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+ mOutputMediaItemListProxy.removeMutingExpectedDevices();
+ assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem1);
+ assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+ }
+
+ private List<MediaDevice> getMediaDevices(List<MediaItem> mediaItems) {
+ return mediaItems.stream()
+ .map(item -> item.getMediaDevice().orElse(null))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 92b26ea3a8ef..7e42ec7e83b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -30,6 +30,7 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -55,19 +56,21 @@ class DragAndDropTest : SysuiTestCase() {
listState: EditTileListState,
onSetTiles: (List<TileSpec>) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = onSetTiles,
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = onSetTiles,
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e76be82c9340..9d4a425c678b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -33,6 +33,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -56,19 +57,22 @@ class EditModeTest : SysuiTestCase() {
var tiles by remember { mutableStateOf(TestEditTiles) }
val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = otherTiles,
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = { tiles = tiles.add(it) },
- onRemoveTile = { tiles = tiles.remove(it) },
- onSetTiles = {},
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = otherTiles,
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = { tiles = tiles.add(it) },
+ onRemoveTile = { tiles = tiles.remove(it) },
+ onSetTiles = {},
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 021be3235ee1..5e76000cc7f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -61,19 +62,21 @@ class ResizingTest : SysuiTestCase() {
listState: EditTileListState,
onResize: (TileSpec, Boolean) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = {},
- onResize = onResize,
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = {},
+ onResize = onResize,
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
index a8bfbd18d0c5..356d445ab4d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -40,9 +40,9 @@ import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.android.wifitrackerlib.WifiEntry
@@ -66,7 +66,7 @@ import org.mockito.kotlin.whenever
@EnableFlags(Flags.FLAG_QS_TILE_DETAILED_VIEW)
@UiThreadTest
class InternetDetailsContentManagerTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val handler: Handler = kosmos.fakeExecutorHandler
private val scope: CoroutineScope = mock<CoroutineScope>()
private val telephonyManager: TelephonyManager = kosmos.telephonyManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index cf54df8565d3..997cf417fe10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -22,6 +22,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceStateManager
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
@@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -59,6 +61,7 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
fakeRearDisplayStateInteractor,
kosmos.rearDisplayInnerDialogDelegateFactory,
kosmos.testScope,
+ kosmos.keyguardUpdateMonitor,
)
@Before
@@ -96,6 +99,26 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
}
}
+ @Test
+ @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+ fun testDialogResumesAfterKeyguardGone() =
+ kosmos.runTest {
+ impl.use {
+ it.start()
+ fakeRearDisplayStateInteractor.emitRearDisplay()
+
+ verify(mockDialog).show()
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(true)
+ // Do not need to check that the dialog is dismissed, since SystemUIDialog
+ // implementation handles that. We just toggle keyguard here so that the flow
+ // emits.
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(false)
+ verify(mockDialog, times(2)).show()
+ }
+ }
+
private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
RearDisplayStateInteractor {
private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index e0118b18ff64..9b03833fd1b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.PowerManager
import android.os.UserManager
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableContext
import android.testing.TestableLooper
import android.view.Display
@@ -29,17 +30,23 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.app.AssistUtils
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.kosmos.testScope
import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
import com.android.systemui.model.sysUiState
+import com.android.systemui.model.sysUiStateFactory
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.views.NavigationBar
import com.android.systemui.process.ProcessWrapper
import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
import com.android.systemui.settings.FakeDisplayTracker
@@ -56,18 +63,20 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.testKosmos
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.back.BackAnimation
import com.android.wm.shell.sysui.ShellInterface
import com.google.common.util.concurrent.MoreExecutors
import java.util.Optional
import java.util.concurrent.Executor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
@@ -81,6 +90,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
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -96,8 +106,10 @@ class LauncherProxyServiceTest : SysuiTestCase() {
private val displayTracker = FakeDisplayTracker(mContext)
private val fakeSystemClock = FakeSystemClock()
private val sysUiState = kosmos.sysUiState
+ private val sysUiStateFactory = kosmos.sysUiStateFactory
private val wakefulnessLifecycle =
WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
+ private val sysuiStatePerDisplayRepository = kosmos.fakeSysUIStatePerDisplayRepository
@Mock private lateinit var launcherProxy: ILauncherProxy.Stub
@Mock private lateinit var packageManager: PackageManager
@@ -149,6 +161,8 @@ class LauncherProxyServiceTest : SysuiTestCase() {
// return isSystemUser as true by default.
`when`(processWrapper.isSystemUser).thenReturn(true)
+ sysuiStatePerDisplayRepository.add(Display.DEFAULT_DISPLAY, sysUiState)
+ runBlocking { kosmos.displayRepository.apply { addDisplay(0) } }
subject = createLauncherProxyService(context)
}
@@ -249,6 +263,48 @@ class LauncherProxyServiceTest : SysuiTestCase() {
verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
+ @Test
+ fun notifySysUiStateFlagsForAllDisplays_triggersUpdateInAllDisplays() =
+ kosmos.testScope.runTest {
+ kosmos.displayRepository.apply {
+ addDisplay(0)
+ addDisplay(1)
+ addDisplay(2)
+ }
+ kosmos.fakeSysUIStatePerDisplayRepository.apply {
+ add(1, sysUiStateFactory.create(1))
+ add(2, sysUiStateFactory.create(2))
+ }
+ clearInvocations(launcherProxy)
+ subject.notifySysUiStateFlagsForAllDisplays()
+
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(0))
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(1))
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(2))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun updateSystemUiStateFlags_updatesAllNavBars() =
+ kosmos.testScope.runTest {
+ kosmos.displayRepository.apply {
+ addDisplay(0)
+ addDisplay(1)
+ }
+ kosmos.fakeSysUIStatePerDisplayRepository.apply {
+ add(1, sysUiStateFactory.create(1))
+ }
+ val navBar0 = mock<NavigationBar>()
+ val navBar1 = mock<NavigationBar>()
+ whenever(navBarController.getNavigationBar(eq(0))).thenReturn(navBar0)
+ whenever(navBarController.getNavigationBar(eq(1))).thenReturn(navBar1)
+
+ subject.updateSystemUiStateFlags()
+
+ verify(navBar0).updateSystemUiStateFlags()
+ verify(navBar1).updateSystemUiStateFlags()
+ }
+
private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
return LauncherProxyService(
ctx,
@@ -260,7 +316,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
screenPinningRequest,
navModeController,
statusBarWinController,
- sysUiState,
+ kosmos.fakeSysUIStatePerDisplayRepository,
mock(),
mock(),
userTracker,
@@ -276,6 +332,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
broadcastDispatcher,
backAnimation,
processWrapper,
+ kosmos.displayRepository,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
index c231be181977..826c54787662 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
@@ -23,6 +23,7 @@ import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
import android.os.UserHandle;
+import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -37,36 +38,34 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class RingtonePlayerTest extends SysuiTestCase {
- private AudioManager mAudioManager;
-
private static final String TAG = "RingtonePlayerTest";
- @Before
- public void setup() throws Exception {
- mAudioManager = getContext().getSystemService(AudioManager.class);
- }
-
@Test
public void testRingtonePlayerUriUserCheck() {
- android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
- final AudioAttributes aa = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
- // get a UserId that doesn't belong to mine
- final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
- // build a URI that I shouldn't have access to
- final Uri uri = new Uri.Builder()
- .scheme("content").authority(otherUserId + "@media")
- .appendPath("external").appendPath("downloads")
- .appendPath("bogusPathThatDoesNotMatter.mp3")
- .build();
- if (android.media.audio.Flags.ringtoneUserUriCheck()) {
- assertThrows(SecurityException.class, () ->
- irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
- );
+ // temporarily skipping this test
+ Log.i(TAG, "skipping testRingtonePlayerUriUserCheck");
+ return;
- assertThrows(SecurityException.class, () ->
- irp.getTitle(uri));
- }
+ // TODO change how IRingtonePlayer is created
+// android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
+// final AudioAttributes aa = new AudioAttributes.Builder()
+// .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+// // get a UserId that doesn't belong to mine
+// final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
+// // build a URI that I shouldn't have access to
+// final Uri uri = new Uri.Builder()
+// .scheme("content").authority(otherUserId + "@media")
+// .appendPath("external").appendPath("downloads")
+// .appendPath("bogusPathThatDoesNotMatter.mp3")
+// .build();
+// if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+// assertThrows(SecurityException.class, () ->
+// irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
+// );
+//
+// assertThrows(SecurityException.class, () ->
+// irp.getTitle(uri));
+// }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index b7040ee2a11e..020b5dd054b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -35,6 +35,7 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -61,10 +62,6 @@ import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
-private fun <T> anyObject(): T {
- return Mockito.anyObject<T>()
-}
-
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
@@ -265,7 +262,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
verify(centralSurfaces)
- .showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ .showBouncerWithDimissAndCancelIfKeyguard(nullable(), nullable())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 3937d3d46d68..ff17a362eb32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -97,7 +96,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
fun themeChangePropagatesToEntry() {
configurationListener.onThemeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -105,7 +103,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
fun densityChangePropagatesToEntry() {
configurationListener.onDensityOrFontScaleChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -129,7 +126,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -160,7 +156,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -196,14 +191,7 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
}
-
- private fun checkGutsExposedCalled() {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- verify(entry).areGutsExposed()
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 84f39be2eeed..2ea4e7f67b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -61,6 +61,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.CachingIconView;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
@@ -71,12 +72,18 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -173,6 +180,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testUpdateBackgroundColors_isRecursive() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
group.setTintColor(Color.RED);
@@ -597,14 +605,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
public void testGetIsNonblockable() throws Exception {
ExpandableNotificationRow row =
mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
- row.setEntry(null);
+ row.setEntryLegacy(null);
assertTrue(row.getIsNonblockable());
NotificationEntry entry = mock(NotificationEntry.class);
Mockito.doReturn(false, true).when(entry).isBlockable();
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
assertTrue(row.getIsNonblockable());
assertFalse(row.getIsNonblockable());
}
@@ -939,12 +947,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
@@ -954,12 +964,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(false);
// THEN
@@ -968,12 +980,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
// THEN
@@ -982,13 +996,15 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setIgnoreLockscreenConstraints(true);
@@ -996,15 +1012,35 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(row.isExpanded()).isTrue();
}
+ private static void setRowPromotedOngoing(ExpandableNotificationRow row) {
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ when(entry.isPromotedOngoing()).thenReturn(true);
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = new NotificationEntryAdapter(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class),
+ mock(NotificationActionClickManager.class),
+ entry);
+ row.setEntryAdapter(entryAdapter);
+ } else {
+ row.setEntryLegacy(entry);
+ }
+ }
+
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(true);
@@ -1014,13 +1050,15 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index c874bc6056c6..cf8278eb8ac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row
import android.annotation.DimenRes
import android.content.res.Resources
import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
import android.service.notification.StatusBarNotification
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -93,13 +94,12 @@ class NotificationContentViewTest : SysuiTestCase() {
/* attrs= */ null,
UserHandle.CURRENT
).apply {
- entry = mockEntry
entryAdapter = mockEntryAdapter
}
}
false -> {
ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
- entry = mockEntry
+ entryLegacy = mockEntry
}
}
}
@@ -402,6 +402,7 @@ class NotificationContentViewTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun setExpandedChild_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should not be shown for the given NotificationEntry
@@ -429,6 +430,7 @@ class NotificationContentViewTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun setExpandedChild_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should be shown for the given NotificationEntry
@@ -458,6 +460,7 @@ class NotificationContentViewTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
val mockNotificationEntry = createMockNotificationEntry()
@@ -486,6 +489,7 @@ class NotificationContentViewTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
val mockNotificationEntry = createMockNotificationEntry()
@@ -514,7 +518,7 @@ class NotificationContentViewTest : SysuiTestCase() {
// Given: controller says bubbles are enabled for the user
view.setBubblesEnabledForUser(true)
- // Then: bottom margin of actionListMarginTarget should not change, still be 20
+ // Then: bottom margin of actionListMarginTarget should be changed to 0
assertEquals(0, getMarginBottom(actionListMarginTarget))
}
@@ -628,8 +632,7 @@ class NotificationContentViewTest : SysuiTestCase() {
whenever(sbnMock.user).thenReturn(userMock)
}
- private fun createMockNotificationEntryAdapter() =
- mock<EntryAdapter>()
+ private fun createMockNotificationEntryAdapter() = mock<EntryAdapter>()
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
val outerLayout = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 0c0ef9d5edfe..10de86644015 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -146,6 +147,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
@Mock private lateinit var notificationManager: INotificationManager
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -185,6 +187,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -297,45 +300,6 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
}
@Test
- fun testChangeDensityOrFontScale() {
- val guts = spy(NotificationGuts(mContext))
- whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
- handler.post((invocation.arguments[0] as Runnable))
- null
- }
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing()
- .whenever(guts)
- .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
- val realRow = createTestNotificationRow()
- val menuItem = createTestMenuItem(realRow)
- val row = spy(realRow)
- whenever(row!!.windowToken).thenReturn(Binder())
- whenever(row.guts).thenReturn(guts)
- doNothing().whenever(row).ensureGutsInflated()
- val realEntry = realRow!!.entry
- val entry = spy(realEntry)
- whenever(entry.row).thenReturn(row)
- whenever(entry.getGuts()).thenReturn(guts)
- Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- executor.runAllReady()
- verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any())
- row.onDensityOrFontScaleChanged()
- gutsManager.onDensityOrFontScaleChanged(entry)
- executor.runAllReady()
- gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
- verify(guts)
- .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any())
- }
-
- @Test
fun testAppOpsSettingsIntent_camera() {
val ops = ArraySet<Int>()
ops.add(AppOpsManager.OP_CAMERA)
@@ -427,6 +391,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -438,6 +403,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -447,7 +413,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(true), /* wasShownHighPriority */
+ eq(true),
+ eq(true),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -462,6 +429,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -473,6 +441,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -482,7 +451,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -497,6 +467,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -508,6 +479,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -517,7 +489,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index 2e65478714af..12f7af106d10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -108,7 +108,7 @@ class FoldStateListenerTest : SysuiTestCase() {
private fun setGoToSleepStates(vararg states: Int) {
mContext.orCreateTestableResources.addOverride(
R.array.config_deviceStatesOnWhichToSleep,
- states
+ states,
)
}
@@ -117,9 +117,10 @@ class FoldStateListenerTest : SysuiTestCase() {
}
companion object {
- private val DEVICE_STATE_FOLDED = Kosmos().foldedDeviceStateList.first()
- private val DEVICE_STATE_HALF_FOLDED = Kosmos().halfFoldedDeviceState
- private val DEVICE_STATE_UNFOLDED = Kosmos().unfoldedDeviceState
+ private val kosmos = Kosmos()
+ private val DEVICE_STATE_FOLDED = kosmos.foldedDeviceStateList.first()
+ private val DEVICE_STATE_HALF_FOLDED = kosmos.halfFoldedDeviceState
+ private val DEVICE_STATE_UNFOLDED = kosmos.unfoldedDeviceState
private const val FOLDED = true
private const val NOT_FOLDED = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index ffb861db182c..063b546cbae9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -296,6 +296,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
@@ -1247,6 +1248,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
index c22c62825d04..293e1fdd6d46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -57,7 +57,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
+import org.mockito.ArgumentMatchers.any
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -138,7 +138,7 @@ class DeviceControlsControllerImplTest : SysuiTestCase() {
`when`(secureSettings.getInt(Settings.Secure.CONTROLS_ENABLED, 1)).thenReturn(0)
controller.setCallback(callback)
- verify(controlsListingController, never()).addCallback(anyObject())
+ verify(controlsListingController, never()).addCallback(any())
verify(callback).onControlsUpdate(null)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index a553b176c34a..6618843ab46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -26,9 +26,8 @@ import com.android.internal.util.LatencyTracker
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.halfFoldedDeviceState
import com.android.systemui.keyguard.ScreenLifecycle
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.unfoldedDeviceState
@@ -70,12 +69,10 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(deviceStateManager.supportedDeviceStates).thenReturn(
- listOf(
- Kosmos().foldedDeviceStateList[0],
- Kosmos().unfoldedDeviceState
+ whenever(deviceStateManager.supportedDeviceStates)
+ .thenReturn(
+ listOf(testKosmos().foldedDeviceStateList[0], testKosmos().unfoldedDeviceState)
)
- )
unfoldLatencyTracker =
UnfoldLatencyTracker(
@@ -85,7 +82,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
context.mainExecutor,
context,
context.contentResolver,
- screenLifecycle
+ screenLifecycle,
)
.apply { init() }
@@ -206,7 +203,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
Settings.Global.putString(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE,
- durationScale.toString()
+ durationScale.toString(),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 9440280649dd..54ac3bf8220a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
@@ -50,11 +52,7 @@ import org.junit.runner.RunWith
class PairwiseFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
- assertThatFlow((1..3).asFlow().pairwise())
- .emitsExactly(
- WithPrev(1, 2),
- WithPrev(2, 3),
- )
+ assertThatFlow((1..3).asFlow().pairwise()).emitsExactly(WithPrev(1, 2), WithPrev(2, 3))
}
@Test fun notEnough() = runBlocking { assertThatFlow(flowOf(1).pairwise()).emitsNothing() }
@@ -157,48 +155,27 @@ class SetChangesFlowTest : SysuiTestCase() {
fun simple() = runBlocking {
assertThatFlow(flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges())
.emitsExactly(
- SetChanges(
- added = setOf(1, 2, 3),
- removed = emptySet(),
- ),
- SetChanges(
- added = setOf(4),
- removed = setOf(1),
- ),
+ SetChanges(added = setOf(1, 2, 3), removed = emptySet()),
+ SetChanges(added = setOf(4), removed = setOf(1)),
)
}
@Test
fun onlyOneEmission() = runBlocking {
assertThatFlow(flowOf(setOf(1)).setChanges())
- .emitsExactly(
- SetChanges(
- added = setOf(1),
- removed = emptySet(),
- )
- )
+ .emitsExactly(SetChanges(added = setOf(1), removed = emptySet()))
}
@Test
fun fromEmptySet() = runBlocking {
assertThatFlow(flowOf(emptySet(), setOf(1, 2)).setChanges())
- .emitsExactly(
- SetChanges(
- removed = emptySet(),
- added = setOf(1, 2),
- )
- )
+ .emitsExactly(SetChanges(removed = emptySet(), added = setOf(1, 2)))
}
@Test
fun dontEmitFirstEvent() = runBlocking {
assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
- .emitsExactly(
- SetChanges(
- removed = setOf(1),
- added = setOf(3),
- )
- )
+ .emitsExactly(SetChanges(removed = setOf(1), added = setOf(3)))
}
}
@@ -235,11 +212,7 @@ class SampleFlowTest : SysuiTestCase() {
emit(4)
}
assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
- .emitsExactly(
- 2 to 1,
- 3 to 3,
- 4 to 3,
- )
+ .emitsExactly(2 to 1, 3 to 3, 4 to 3)
}
}
@@ -419,10 +392,262 @@ class ThrottleFlowTest : SysuiTestCase() {
}
}
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SlidingWindowFlowTest : SysuiTestCase() {
+
+ @Test
+ fun basicWindowing() = runTest {
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<List<Int>>()
+ val collectJob =
+ backgroundScope.launch {
+ (1..5)
+ .asFlow()
+ .onEach { delay(100) }
+ .slidingWindow(300.milliseconds, choreographer.fakeClock)
+ .toList(output)
+ }
+
+ choreographer.advanceAndRun(0)
+ assertThat(output).isEmpty()
+
+ choreographer.advanceAndRun(100)
+ assertThat(output).containsExactly(listOf(1))
+
+ choreographer.advanceAndRun(1)
+ assertThat(output).containsExactly(listOf(1))
+
+ choreographer.advanceAndRun(99)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+ choreographer.advanceAndRun(100)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+ choreographer.advanceAndRun(100)
+ assertThat(output)
+ .containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3, 4))
+
+ choreographer.advanceAndRun(100)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3, 4),
+ listOf(3, 4, 5),
+ )
+
+ choreographer.advanceAndRun(100)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3, 4),
+ listOf(3, 4, 5),
+ listOf(4, 5),
+ )
+
+ choreographer.advanceAndRun(100)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3, 4),
+ listOf(3, 4, 5),
+ listOf(4, 5),
+ listOf(5),
+ )
+
+ choreographer.advanceAndRun(100)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3, 4),
+ listOf(3, 4, 5),
+ listOf(4, 5),
+ listOf(5),
+ emptyList<Int>(),
+ )
+
+ // Verify no more emissions
+ choreographer.advanceAndRun(9999999999)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3, 4),
+ listOf(3, 4, 5),
+ listOf(4, 5),
+ listOf(5),
+ emptyList<Int>(),
+ )
+
+ assertThat(collectJob.isCompleted).isTrue()
+ }
+
+ @Test
+ fun initialEmptyFlow() = runTest {
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<List<Int>>()
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ delay(200)
+ emit(1)
+ }
+ .slidingWindow(100.milliseconds, choreographer.fakeClock)
+ .toList(output)
+ }
+
+ choreographer.advanceAndRun(0)
+ assertThat(output).isEmpty()
+
+ choreographer.advanceAndRun(200)
+ assertThat(output).containsExactly(listOf(1))
+
+ choreographer.advanceAndRun(100)
+ assertThat(output).containsExactly(listOf(1), emptyList<Int>())
+
+ assertThat(collectJob.isCompleted).isTrue()
+ }
+
+ @Test
+ fun windowLargerThanData() = runTest {
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<List<Int>>()
+ val collectJob =
+ backgroundScope.launch {
+ (1..3)
+ .asFlow()
+ .onEach { delay(50) }
+ .slidingWindow(500.milliseconds, choreographer.fakeClock)
+ .toList(output)
+ }
+
+ choreographer.advanceAndRun(0)
+ assertThat(output).isEmpty()
+
+ choreographer.advanceAndRun(50)
+ assertThat(output).containsExactly(listOf(1))
+
+ choreographer.advanceAndRun(50)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+ choreographer.advanceAndRun(50)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+ // It has been 100ms since the first emission, which means we have 400ms left until the
+ // first item is evicted from the window. Ensure that we have no evictions until that time.
+ choreographer.advanceAndRun(399)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+ choreographer.advanceAndRun(1)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3))
+
+ choreographer.advanceAndRun(50)
+ assertThat(output)
+ .containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3), listOf(3))
+
+ choreographer.advanceAndRun(50)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(1, 2, 3),
+ listOf(2, 3),
+ listOf(3),
+ emptyList<Int>(),
+ )
+
+ assertThat(collectJob.isCompleted).isTrue()
+ }
+
+ @Test
+ fun dataGapLargerThanWindow() = runTest {
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<List<Int>>()
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(200)
+ emit(2)
+ delay(500) // Gap larger than window
+ emit(3)
+ }
+ .slidingWindow(300.milliseconds, choreographer.fakeClock)
+ .toList(output)
+ }
+
+ choreographer.advanceAndRun(0)
+ assertThat(output).containsExactly(listOf(1))
+
+ choreographer.advanceAndRun(200)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+ choreographer.advanceAndRun(100)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(2))
+
+ choreographer.advanceAndRun(200)
+ assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(2), emptyList<Int>())
+
+ choreographer.advanceAndRun(200)
+ assertThat(output)
+ .containsExactly(listOf(1), listOf(1, 2), listOf(2), emptyList<Int>(), listOf(3))
+
+ choreographer.advanceAndRun(300)
+ assertThat(output)
+ .containsExactly(
+ listOf(1),
+ listOf(1, 2),
+ listOf(2),
+ emptyList<Int>(),
+ listOf(3),
+ emptyList<Int>(),
+ )
+
+ assertThat(collectJob.isCompleted).isTrue()
+ }
+
+ @Test
+ fun emptyFlow() = runTest {
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<List<Int>>()
+
+ val collectJob =
+ backgroundScope.launch {
+ emptyFlow<Int>().slidingWindow(100.milliseconds).toList(output)
+ }
+
+ choreographer.advanceAndRun(0)
+ assertThat(output).isEmpty()
+
+ assertThat(collectJob.isCompleted).isTrue()
+ }
+
+ private fun createChoreographer(testScope: TestScope) =
+ object {
+ val fakeClock = FakeSystemClock()
+
+ fun advanceAndRun(millis: Long) {
+ fakeClock.advanceTime(millis)
+ testScope.advanceTimeBy(millis)
+ testScope.runCurrent()
+ }
+ }
+}
+
private fun <T> assertThatFlow(flow: Flow<T>) =
object {
suspend fun emitsExactly(vararg emissions: T) =
assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+
suspend fun emitsNothing() = assertThat(flow.toList()).isEmpty()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index 5f3442048fcd..422b20e8b951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -45,7 +45,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@SmallTest
@@ -92,7 +92,7 @@ class GradientColorWallpaperTest : SysuiTestCase() {
engine.onSurfaceRedrawNeeded(surfaceHolder)
- verifyZeroInteractions(canvas)
+ verifyNoMoreInteractions(canvas)
}
@Test
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 663a85330f70..338e4bec7aa2 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
@@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import org.mockito.Mockito.`when` as whenever
/** Creates a mock display. */
@@ -41,8 +42,8 @@ fun display(type: Int, flags: Int = 0, id: Int = 0, state: Int? = null): Display
}
/** Creates a mock [DisplayRepository.PendingDisplay]. */
-fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
- mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+fun createPendingDisplay(id: Int = 0): PendingDisplay =
+ mock<PendingDisplay> { whenever(this.id).thenReturn(id) }
@SysUISingleton
/** Fake [DisplayRepository] implementation for testing. */
@@ -50,7 +51,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
private val pendingDisplayFlow =
- MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
+ MutableSharedFlow<PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
@@ -101,7 +102,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
suspend fun emit(value: Set<Display>) = flow.emit(value)
/** Emits [value] as [pendingDisplay] flow value. */
- suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
+ suspend fun emit(value: PendingDisplay?) = pendingDisplayFlow.emit(value)
override val displays: StateFlow<Set<Display>>
get() = flow
@@ -109,7 +110,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
override val displayIds: StateFlow<Set<Int>>
get() = displayIdFlow
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
+ override val pendingDisplay: Flow<PendingDisplay?>
get() = pendingDisplayFlow
private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index aa23aa30b7bc..5ab3b3de49f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -66,6 +66,7 @@ class FakePerDisplayInstanceProviderWithTeardown :
val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
+val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
val Kosmos.fakePerDisplayInstanceRepository by
Kosmos.Fixture {
PerDisplayInstanceRepositoryImpl(
@@ -73,6 +74,6 @@ val Kosmos.fakePerDisplayInstanceRepository by
instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
testScope.backgroundScope,
displayRepository,
- dumpManager,
+ perDisplayDumpHelper,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
index 3cec5a9cd822..361d21dc134d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-val Kosmos.keyguardServiceShowLockscreenInteractor by
- Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.keyguardServiceShowLockscreenRepository by
+ Kosmos.Fixture { KeyguardServiceShowLockscreenRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
new file mode 100644
index 000000000000..447aa1255463
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardServiceShowLockscreenRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardServiceShowLockscreenInteractor by
+ Kosmos.Fixture {
+ KeyguardServiceShowLockscreenInteractor(
+ backgroundScope = testScope,
+ selectedUserInteractor = selectedUserInteractor,
+ repository = keyguardServiceShowLockscreenRepository,
+ userTracker = userTracker,
+ wmLockscreenVisibilityInteractor = { windowManagerLockscreenVisibilityInteractor },
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
index f7caeb69f17e..dd868e767d9b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
@@ -18,10 +18,12 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
val Kosmos.keyguardShowWhileAwakeInteractor by
Kosmos.Fixture {
KeyguardShowWhileAwakeInteractor(
+ backgroundScope = testScope,
biometricSettingsRepository = biometricSettingsRepository,
keyguardEnabledInteractor = keyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor = keyguardServiceShowLockscreenInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 43fa718cff9a..c89fb705f417 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -27,7 +27,7 @@ import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.systemClock
-val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+val Kosmos.keyguardWakeDirectlyToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor by
Kosmos.Fixture {
KeyguardWakeDirectlyToGoneInteractor(
applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
index 11bd4c7b7940..54261c7f622b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
@@ -38,7 +38,9 @@ val Kosmos.sysUiStateFactory by Fixture {
}
}
-val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture { FakePerDisplayRepository<SysUiState>() }
+val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture {
+ FakePerDisplayRepository<SysUiState>().apply { add(Display.DEFAULT_DISPLAY, sysUiState) }
+}
val Kosmos.sysuiStateInteractor by Fixture {
SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository, displayRepository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 4efcada96a14..215df9d59ec9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -58,7 +58,7 @@ public class GroupEntryBuilder {
return this;
}
- /** Sets the creation time. */
+ /** Sets the creation time. Should be SystemClock.elapsedRealtime */
public GroupEntryBuilder setCreationTime(long creationTime) {
mCreationTime = creationTime;
return this;
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
index 626a68f6a59d..38b59945ab7d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.display.data
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
-sealed interface DisplayEvent {
- val displayId: Int
- data class Added(override val displayId: Int) : DisplayEvent
- data class Removed(override val displayId: Int) : DisplayEvent
- data class Changed(override val displayId: Int) : DisplayEvent
-}
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.packageDemotionInteractor by Kosmos.Fixture { PackageDemotionInteractor() }
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 1148539187ac..083d2aa15316 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -180,9 +180,11 @@
// AUTO-GENERATED-END
],
"ravenwood-postsubmit": [
- {
- "name": "SystemUiRavenTests",
- "host": true
- }
+ // We haven't maintained SystemUiRavenTests, and as a result, it's been demoted already.
+ // Disable it until we fix the issues: b/319647875
+ // {
+ // "name": "SystemUiRavenTests",
+ // "host": true
+ // }
]
}
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 d935626c34df..d8741975c71a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -23,7 +23,9 @@ import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACK
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;
@@ -271,6 +273,13 @@ public class RavenwoodRuntimeEnvironmentController {
dumpJavaProperties();
dumpOtherInfo();
+ System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
+ var runtimePath = getRavenwoodRuntimePath();
+ System.setProperty(RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP, runtimePath);
+
+ Log.i(TAG, "PWD=" + System.getProperty("user.dir"));
+ Log.i(TAG, "RuntimePath=" + runtimePath);
+
// Make sure libravenwood_runtime is loaded.
System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
@@ -314,7 +323,6 @@ public class RavenwoodRuntimeEnvironmentController {
Typeface.loadPreinstalledSystemFontMap();
Typeface.loadNativeSystemFonts();
- System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
// This will let AndroidJUnit4 use the original runner.
System.setProperty("android.junit.runner",
"androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 893b354d4645..e1b537e11842 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -68,6 +68,8 @@ public class RavenwoodCommonUtils {
RAVENWOOD_RUNTIME_PATH + "ravenwood-data/ravenwood-empty-res.apk";
public static final String RAVENWOOD_VERSION_JAVA_SYSPROP = "android.ravenwood.version";
+ public static final String RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP =
+ "android.ravenwood.runtime_path";
/**
* @return if we're running on Ravenwood.
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
index fd6d6fb66465..b3af8753ee83 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
@@ -25,7 +25,6 @@ import com.android.internal.os.Flags
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,7 +39,6 @@ class RavenwoodAconfigSimpleReadTests {
}
@Test
- @Ignore // TODO: Enable this test after rolling out the "2" flags.
fun testTrueFlags() {
assertTrue(Flags.ravenwoodFlagRo2())
assertTrue(Flags.ravenwoodFlagRw2())
@@ -67,14 +65,12 @@ class RavenwoodAconfigCheckFlagsRuleTests {
@Test
@RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RO_2)
- @Ignore // TODO: Enable this test after rolling out the "2" flags.
fun testRequireFlagsDisabledRo() {
fail("This test shouldn't be executed")
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RW_2)
- @Ignore // TODO: Enable this test after rolling out the "2" flags.
fun testRequireFlagsDisabledRw() {
fail("This test shouldn't be executed")
}
diff --git a/ravenwood/tests/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp
index c14576550f78..960b3ed0013a 100644
--- a/ravenwood/tests/resapk_test/Android.bp
+++ b/ravenwood/tests/resapk_test/Android.bp
@@ -10,7 +10,7 @@ package {
android_ravenwood_test {
name: "RavenwoodResApkTest",
- resource_apk: "RavenwoodResApkTest-apk",
+ resource_apk: "RavenwoodResApkTest-res",
libs: [
// Normally, tests shouldn't directly access it, but we need to access RavenwoodCommonUtils
@@ -24,6 +24,7 @@ android_ravenwood_test {
],
srcs: [
"test/**/*.java",
+ ":RavenwoodResApkTest-res{.aapt.srcjar}",
],
sdk_version: "test_current",
auto_gen_config: true,
diff --git a/ravenwood/tests/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp
index 10ed5e2f8410..fd8976df4316 100644
--- a/ravenwood/tests/resapk_test/apk/Android.bp
+++ b/ravenwood/tests/resapk_test/apk/Android.bp
@@ -8,7 +8,13 @@ package {
}
android_app {
- name: "RavenwoodResApkTest-apk",
+ name: "RavenwoodResApkTest-res",
sdk_version: "current",
+
+ use_resource_processor: false,
+
+ flags_packages: [
+ "com.android.internal.os.flags-aconfig",
+ ],
}
diff --git a/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml b/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml
new file mode 100644
index 000000000000..17cdb868fc6b
--- /dev/null
+++ b/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <View android:id="@+id/view1" text="no-flags" />
+ <View android:id="@+id/view2" text="ro-enabled" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_2"/>
+ <View android:id="@+id/view3" text="ro-disabled" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_1"/>
+ <View android:id="@+id/view2" text="rw-enabled" android:featureFlag="com.android.internal.os.ravenwood_flag_rw_2"/>
+ <View android:id="@+id/view3" text="rw-disabled" android:featureFlag="com.android.internal.os.ravenwood_flag_rw_1"/>
+</LinearLayout>
diff --git a/ravenwood/tests/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
index 23d4c0f21007..5abf7475caa7 100644
--- a/ravenwood/tests/resapk_test/apk/res/values/strings.xml
+++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
@@ -13,7 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Test string 1 -->
<string name="test_string_1" translatable="false" >Test String 1</string>
+ <!-- values can only use readonly flags -->
+ <string name="test_string_enabled" translatable="false" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_2">Enabled</string>
+ <string name="test_string_disabled" translatable="false" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_1">Disabled</string>
</resources>
diff --git a/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
index e547114bbe40..89f8d40da7d4 100644
--- a/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
+++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
@@ -16,23 +16,41 @@
package com.android.ravenwoodtest.resapk_test;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.util.Log;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.ravenwood.common.RavenwoodCommonUtils;
+import com.android.ravenwood.restest_apk.R;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
public class RavenwoodResApkTest {
+ private static final String TAG = "RavenwoodResApkTest";
+
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+
/**
* Ensure the file "ravenwood-res.apk" exists.
- * TODO Check the content of it, once Ravenwood supports resources. The file should
- * be a copy of RavenwoodResApkTest-apk.apk
*/
@Test
public void testResApkExists() {
@@ -48,4 +66,73 @@ public class RavenwoodResApkTest {
assertTrue(new File(
RavenwoodCommonUtils.getRavenwoodRuntimePath() + "/" + file).exists());
}
+
+ @Test
+ public void testReadStringNoFlag() {
+ assertThat(sContext.getString(R.string.test_string_1)).isEqualTo("Test String 1");
+ }
+
+ @Test
+ public void testReadStringRoFlagEnabled() {
+ assertThat(sContext.getString(R.string.test_string_enabled)).isEqualTo("Enabled");
+ }
+
+ @Test
+ public void testReadStringRoFlagDisabled() {
+ assertThrows(android.content.res.Resources.NotFoundException.class, () -> {
+ sContext.getString(R.string.test_string_disabled);
+ });
+ }
+
+ /**
+ * Look into the layout and collect the "text" attribute.
+ *
+ * It _should_ respect android:featureFlag, but until b/396458006 gets fixed, this returns
+ * even disabled elements.
+ */
+ private List<String> getTextsFromEnabledChildren() throws Exception {
+ try (XmlResourceParser parser = sContext.getResources().getLayout(R.layout.testlayout)) {
+ assertNotNull(parser);
+
+ var ret = new ArrayList<String>();
+
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ var text = parser.getAttributeValue(null, "text");
+ if (text == null) {
+ continue;
+ }
+
+ Log.d(TAG, "Found tag: " + parser.getName() + " text='" + text + "'");
+ ret.add(text);
+ }
+ return ret;
+ }
+ }
+
+ @Test
+ public void testElementNoFlag() throws Exception {
+ assertThat(getTextsFromEnabledChildren()).contains("no-flags");
+ }
+
+ @Test
+ public void testElementWithRoFlagEnabled() throws Exception {
+ assertThat(getTextsFromEnabledChildren()).contains("ro-enabled");
+ }
+
+ @Test
+ public void testElementWithRoFlagDisabled() throws Exception {
+ assertThat(getTextsFromEnabledChildren()).doesNotContain("ro-disabled");
+ }
+
+ @Test
+ public void testElementWithRwFlagEnabled() throws Exception {
+ assertThat(getTextsFromEnabledChildren()).contains("rw-enabled");
+ }
+
+ @Test
+ @DisabledOnRavenwood(bug = 396458006,
+ reason = "RW flags in XML are all handled as enabled for now")
+ public void testElementWithRwFlagDisabled() throws Exception {
+ assertThat(getTextsFromEnabledChildren()).doesNotContain("rw-disabled");
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6c26c1f74002..703e37fad5ad 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -531,7 +531,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilitySecurityPolicy securityPolicy,
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
- AccessibilityDisplayListener a11yDisplayListener,
+ AccessibilityDisplayListener.DisplayManagerWrapper displayManagerWrapper,
MagnificationController magnificationController,
@Nullable AccessibilityInputFilter inputFilter,
ProxyManager proxyManager,
@@ -550,7 +550,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
- mA11yDisplayListener = a11yDisplayListener;
+ mA11yDisplayListener = new AccessibilityDisplayListener(displayManagerWrapper,
+ new MainHandler(Looper.getMainLooper()));
mMagnificationController = magnificationController;
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -596,7 +597,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
this, LocalServices.getService(PackageManagerInternal.class));
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
- mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mA11yDisplayListener = new AccessibilityDisplayListener(
+ new AccessibilityDisplayListener.DisplayManagerWrapper(mContext), mMainHandler);
mMagnificationController = new MagnificationController(
this,
mLock,
@@ -5457,11 +5459,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* A Utility class to handle display state.
*/
public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
- private final DisplayManager mDisplayManager;
+ private final DisplayManagerWrapper mDisplayManager;
private final ArrayList<Display> mDisplaysList = new ArrayList<>();
private int mSystemUiUid = 0;
- AccessibilityDisplayListener(Context context, Handler handler) {
+ AccessibilityDisplayListener(DisplayManagerWrapper displayManager, Handler handler) {
// Avoid concerns about one thread adding displays while another thread removes
// them by ensuring the looper is the main looper and the DisplayListener
// callbacks are always executed on the one main thread.
@@ -5474,7 +5476,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.e(LOG_TAG, errorMessage);
}
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager = displayManager;
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -5626,6 +5628,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return true;
}
+
+ /** Wrapper of DisplayManager for testing. */
+ @VisibleForTesting
+ static class DisplayManagerWrapper {
+ private final DisplayManager mDm;
+
+ DisplayManagerWrapper(Context context) {
+ mDm = context.getSystemService(DisplayManager.class);
+ }
+
+ /**
+ * @see DisplayManager#registerDisplayListener(DisplayManager.DisplayListener, Handler)
+ */
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mDm.registerDisplayListener(listener, handler);
+ }
+
+ /** @see DisplayManager#getDisplays() */
+ public Display[] getDisplays() {
+ return mDm.getDisplays();
+ }
+
+ /** @see DisplayManager#getDisplay(int) */
+ public Display getDisplay(int displayId) {
+ return mDm.getDisplay(displayId);
+ }
+ }
}
/** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 805d7f820c8d..94cef418b6c8 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -165,6 +165,9 @@ public class HearingDevicePhoneCallNotificationController {
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+ if (commDevice == null) {
+ return;
+ }
mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
if (mHearingDevice != null) {
showNotificationIfNeeded();
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 23166a800245..84158cf911ad 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -24,6 +24,7 @@ import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_M
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT;
import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
@@ -45,6 +46,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
@@ -799,7 +801,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
final long now = SystemClock.uptimeMillis();
- int actionButton;
+ int actionButton = BUTTON_PRIMARY;
if (mHoveredState) {
// Always triggers left-click when the cursor hovers over the autoclick type
// panel, to always allow users to change a different click type. Otherwise, if
@@ -807,15 +809,32 @@ public class AutoclickController extends BaseEventStreamTransformation {
// select other click types.
actionButton = BUTTON_PRIMARY;
} else {
- actionButton = mActiveClickType == AUTOCLICK_TYPE_RIGHT_CLICK
- ? BUTTON_SECONDARY
- : BUTTON_PRIMARY;
+ switch (mActiveClickType) {
+ case AUTOCLICK_TYPE_LEFT_CLICK:
+ actionButton = BUTTON_PRIMARY;
+ break;
+ case AUTOCLICK_TYPE_RIGHT_CLICK:
+ actionButton = BUTTON_SECONDARY;
+ break;
+ case AUTOCLICK_TYPE_DOUBLE_CLICK:
+ actionButton = BUTTON_PRIMARY;
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ sendMotionEvent(actionButton, now);
+ sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
+ return;
+ default:
+ break;
+ }
}
+ sendMotionEvent(actionButton, now);
+ }
+
+ private void sendMotionEvent(int actionButton, long eventTime) {
MotionEvent downEvent =
MotionEvent.obtain(
- /* downTime= */ now,
- /* eventTime= */ now,
+ /* downTime= */ eventTime,
+ /* eventTime= */ eventTime,
MotionEvent.ACTION_DOWN,
/* pointerCount= */ 1,
mTempPointerProperties,
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 88b791046c87..6e098d014ee3 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -21,15 +21,18 @@ import android.annotation.Nullable;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
+import android.hardware.SensorAdditionalInfo;
import android.hardware.SensorDirectChannel;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -140,7 +143,7 @@ public class SensorController {
final IBinder sensorToken =
new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
- virtualDevice, sensorToken);
+ config.getFlags(), virtualDevice, sensorToken);
synchronized (mLock) {
mSensorDescriptors.put(sensorToken, sensorDescriptor);
mVirtualSensors.put(handle, sensor);
@@ -164,6 +167,37 @@ public class SensorController {
}
}
+ boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ Objects.requireNonNull(token);
+ Objects.requireNonNull(info);
+ synchronized (mLock) {
+ final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token);
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ if (sensorDescriptor == null) {
+ throw new IllegalArgumentException("Could not send sensor event for given token");
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN,
+ /* serial= */ 0, timestamp++, /* values= */ null)) {
+ return false;
+ }
+ for (int i = 0; i < info.getValues().size(); ++i) {
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), info.getType(), /* serial= */ i,
+ timestamp++, info.getValues().get(i))) {
+ return false;
+ }
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END,
+ /* serial= */ 0, timestamp, /* values= */ null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Nullable
VirtualSensor getSensorByHandle(int handle) {
synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 28efdfc01ee2..0023b6d53837 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -55,6 +55,7 @@ import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtualdevice.flags.Flags;
import android.compat.annotation.ChangeId;
@@ -1294,6 +1295,18 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
@Override // Binder call
+ public boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ checkCallerIsDeviceOwner();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mSensorController.sendSensorAdditionalInfo(token, info);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
IntentFilter filter) {
checkCallerIsDeviceOwner();
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 7eb7072520de..a1d39faa7039 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -313,7 +313,8 @@ public class ContextualSearchManagerService extends SystemService {
android.Manifest.permission.CREATE_USERS,
android.Manifest.permission.QUERY_USERS
})
- private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+ private Intent getContextualSearchIntent(int entrypoint, int userId, String callingPackage,
+ CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
@@ -332,6 +333,9 @@ public class ContextualSearchManagerService extends SystemService {
launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
mAudioManager.isMusicActive());
}
+ if (Flags.selfInvocation()) {
+ launchIntent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+ }
boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
final List<IBinder> activityTokens = new ArrayList<>(records.size());
@@ -500,6 +504,7 @@ public class ContextualSearchManagerService extends SystemService {
}
private void startContextualSearchInternal(int entrypoint) {
+ final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid());
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
mAssistDataRequester.cancel();
// Creates a new CallbackToken at mToken and an expiration handler.
@@ -508,7 +513,8 @@ public class ContextualSearchManagerService extends SystemService {
// server has READ_FRAME_BUFFER permission to get the screenshot and because only
// the system server can invoke non-exported activities.
Binder.withCleanCallingIdentity(() -> {
- Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
+ Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId,
+ callingPackage, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 76ba0054583b..ce5a12179d44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2004,7 +2004,7 @@ public class ActivityManagerService extends IActivityManager.Stub
new IAppOpsCallback.Stub() {
@Override public void opChanged(int op, int uid, String packageName,
String persistentDeviceId) {
- if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
+ if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && uid >= 0) {
if (getAppOpsManager().checkOpNoThrow(op, uid, packageName)
!= AppOpsManager.MODE_ALLOWED) {
runInBackgroundDisabled(uid);
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index a47beae1d9cb..800e2b2d9657 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -394,6 +394,7 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy
private class MyAppOpsCallback extends IAppOpsCallback.Stub {
@Override
public void opChanged(int op, int uid, String packageName, String persistentDeviceId) {
+ if (uid < 0) return;
mHandler.obtainMessage(MyHandler.MSG_APPOPS_CHANGED, op, uid, packageName)
.sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 81d34f67dee9..fe4fa1068bb3 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -41,6 +41,7 @@ import dalvik.annotation.optimization.NeverCompile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
/**
* Tunable parameters for broadcast dispatch policy
@@ -286,6 +287,17 @@ public class BroadcastConstants {
"max_frozen_outgoing_broadcasts";
private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32;
+ /**
+ * For {@link BroadcastQueueImpl}: Indicates how long after a process start was initiated,
+ * it should be considered abandoned and discarded.
+ */
+ public long PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+ DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS * Build.HW_TIMEOUT_MULTIPLIER;
+ private static final String KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+ "pending_cold_start_abandon_timeout_millis";
+ private static final long DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+ TimeUnit.MINUTES.toMillis(5);
+
// Settings override tracking for this instance
private String mSettingsKey;
private SettingsObserver mSettingsObserver;
@@ -434,6 +446,10 @@ public class BroadcastConstants {
MAX_FROZEN_OUTGOING_BROADCASTS = getDeviceConfigInt(
KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS);
+ PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS = getDeviceConfigLong(
+ KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS,
+ DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS)
+ * Build.HW_TIMEOUT_MULTIPLIER;
}
// TODO: migrate BroadcastRecord to accept a BroadcastConstants
@@ -491,6 +507,8 @@ public class BroadcastConstants {
PENDING_COLD_START_CHECK_INTERVAL_MILLIS).println();
pw.print(KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
MAX_FROZEN_OUTGOING_BROADCASTS).println();
+ pw.print(KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS,
+ PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS).println();
pw.decreaseIndent();
pw.println();
}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 508c01802156..c0fe73877c01 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -245,6 +245,24 @@ class BroadcastProcessQueue {
*/
private final ArrayList<BroadcastRecord> mOutgoingBroadcasts = new ArrayList<>();
+ /**
+ * The timestamp, in {@link SystemClock#uptimeMillis()}, at which a cold start was initiated
+ * for the process associated with this queue.
+ *
+ * Note: We could use the already existing {@link ProcessRecord#getStartUptime()} instead
+ * of this, but the need for this timestamp is to identify an issue (b/393898613) where the
+ * suspicion is that process is not attached or getting changed. So, we don't want to rely on
+ * ProcessRecord directly for this purpose.
+ */
+ private long mProcessStartInitiatedTimestampMillis;
+
+ /**
+ * Indicates whether the number of current receivers has been incremented using
+ * {@link ProcessReceiverRecord#incrementCurReceivers()}. This allows to skip decrementing
+ * the receivers when it is not required.
+ */
+ private boolean mCurReceiversIncremented;
+
public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
@NonNull String processName, int uid) {
this.constants = Objects.requireNonNull(constants);
@@ -652,6 +670,52 @@ class BroadcastProcessQueue {
return mActiveFirstLaunch;
}
+ public void incrementCurAppReceivers() {
+ app.mReceivers.incrementCurReceivers();
+ mCurReceiversIncremented = true;
+ }
+
+ public void decrementCurAppReceivers() {
+ if (mCurReceiversIncremented) {
+ app.mReceivers.decrementCurReceivers();
+ mCurReceiversIncremented = false;
+ }
+ }
+
+ public void setProcessStartInitiatedTimestampMillis(@UptimeMillisLong long timestampMillis) {
+ mProcessStartInitiatedTimestampMillis = timestampMillis;
+ }
+
+ @UptimeMillisLong
+ public long getProcessStartInitiatedTimestampMillis() {
+ return mProcessStartInitiatedTimestampMillis;
+ }
+
+ public boolean hasProcessStartInitiationTimedout() {
+ if (mProcessStartInitiatedTimestampMillis <= 0) {
+ return false;
+ }
+ return (SystemClock.uptimeMillis() - mProcessStartInitiatedTimestampMillis)
+ > constants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS;
+ }
+
+ /**
+ * Returns if the process start initiation is expected to be timed out at this point. This
+ * allows us to dump necessary state for debugging before the process start is timed out
+ * and discarded.
+ */
+ public boolean isProcessStartInitiationTimeoutExpected() {
+ if (mProcessStartInitiatedTimestampMillis <= 0) {
+ return false;
+ }
+ return (SystemClock.uptimeMillis() - mProcessStartInitiatedTimestampMillis)
+ > constants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS / 2;
+ }
+
+ public void clearProcessStartInitiatedTimestampMillis() {
+ mProcessStartInitiatedTimestampMillis = 0;
+ }
+
/**
* Get package name of the first application loaded into this process.
*/
@@ -1558,6 +1622,10 @@ class BroadcastProcessQueue {
if (mActiveReEnqueued) {
pw.print("activeReEnqueued:"); pw.println(mActiveReEnqueued);
}
+ if (mProcessStartInitiatedTimestampMillis > 0) {
+ pw.print("processStartInitiatedTimestamp:"); pw.println(
+ TimeUtils.formatUptime(mProcessStartInitiatedTimestampMillis));
+ }
}
@NeverCompile
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index d276b9a94791..6e893ad0a425 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -534,6 +534,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
// skip to look for another warm process
if (mRunningColdStart == null) {
mRunningColdStart = queue;
+ mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
} else if (isPendingColdStartValid()) {
// Move to considering next runnable queue
queue = nextQueue;
@@ -542,6 +543,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
// Pending cold start is not valid, so clear it and move on.
clearInvalidPendingColdStart();
mRunningColdStart = queue;
+ mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
}
}
@@ -588,7 +590,9 @@ class BroadcastQueueImpl extends BroadcastQueue {
@GuardedBy("mService")
private boolean isPendingColdStartValid() {
- if (mRunningColdStart.app.getPid() > 0) {
+ if (mRunningColdStart.hasProcessStartInitiationTimedout()) {
+ return false;
+ } else if (mRunningColdStart.app.getPid() > 0) {
// If the process has already started, check if it wasn't killed.
return !mRunningColdStart.app.isKilled();
} else {
@@ -673,6 +677,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
// We've been waiting for this app to cold start, and it's ready
// now; dispatch its next broadcast and clear the slot
+ mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
mRunningColdStart = null;
// Now that we're running warm, we can finally request that OOM
@@ -756,6 +761,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
// We've been waiting for this app to cold start, and it had
// trouble; clear the slot and fail delivery below
+ mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
mRunningColdStart = null;
// We might be willing to kick off another cold start
@@ -1036,6 +1042,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
"startProcessLocked failed");
return true;
}
+ queue.setProcessStartInitiatedTimestampMillis(SystemClock.uptimeMillis());
// TODO: b/335420031 - cache receiver intent to avoid multiple calls to getReceiverIntent.
mService.mProcessList.getAppStartInfoTracker().handleProcessBroadcastStart(
startTimeNs, queue.app, r.getReceiverIntent(receiver), r.alarm /* isAlarm */);
@@ -1991,6 +1998,32 @@ class BroadcastQueueImpl extends BroadcastQueue {
if (mRunningColdStart != null) {
checkState(getRunningIndexOf(mRunningColdStart) >= 0,
"isOrphaned " + mRunningColdStart);
+
+ final BroadcastProcessQueue queue = getProcessQueue(mRunningColdStart.processName,
+ mRunningColdStart.uid);
+ checkState(queue == mRunningColdStart, "Conflicting " + mRunningColdStart
+ + " with queue " + queue
+ + ";\n mRunningColdStart.app: " + mRunningColdStart.app.toDetailedString()
+ + ";\n queue.app: " + queue.app.toDetailedString());
+
+ checkState(mRunningColdStart.app != null, "Empty cold start queue "
+ + mRunningColdStart);
+
+ if (mRunningColdStart.isProcessStartInitiationTimeoutExpected()) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Process start timeout expected for app ");
+ sb.append(mRunningColdStart.app);
+ sb.append(" in queue ");
+ sb.append(mRunningColdStart);
+ sb.append("; startUpTime: ");
+ final long startupTimeMs =
+ mRunningColdStart.getProcessStartInitiatedTimestampMillis();
+ sb.append(startupTimeMs == 0 ? "<none>"
+ : TimeUtils.formatDuration(startupTimeMs - SystemClock.uptimeMillis()));
+ sb.append(";\n app: ");
+ sb.append(mRunningColdStart.app.toDetailedString());
+ checkState(false, sb.toString());
+ }
}
// Verify health of all known process queues
@@ -2090,7 +2123,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
@GuardedBy("mService")
private void notifyStartedRunning(@NonNull BroadcastProcessQueue queue) {
if (queue.app != null) {
- queue.app.mReceivers.incrementCurReceivers();
+ queue.incrementCurAppReceivers();
// Don't bump its LRU position if it's in the background restricted.
if (mService.mInternal.getRestrictionLevel(
@@ -2115,7 +2148,7 @@ class BroadcastQueueImpl extends BroadcastQueue {
@GuardedBy("mService")
private void notifyStoppedRunning(@NonNull BroadcastProcessQueue queue) {
if (queue.app != null) {
- queue.app.mReceivers.decrementCurReceivers();
+ queue.decrementCurAppReceivers();
if (queue.runningOomAdjusted) {
mService.enqueueOomAdjTargetLocked(queue.app);
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 4b6d6bc955cc..fd2d8352eb4f 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,7 +61,7 @@ per-file *Oom* = file:/OOM_ADJUSTER_OWNERS
per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
# Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
# Activity Security
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index eea667ef2f39..400c699bf93f 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -70,6 +70,7 @@ import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
@@ -1414,6 +1415,16 @@ class ProcessRecord implements WindowProcessListener {
return mStringName = sb.toString();
}
+ String toDetailedString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(this);
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ dump(pw, " ");
+ sb.append(sw);
+ return sb.toString();
+ }
+
/*
* Return true if package has been added false if not
*/
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index cfd22fbdeece..cb4342f27bc8 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -253,11 +253,12 @@ public class SettingsToPropertiesMapper {
"pixel_state_server",
"pixel_system_sw_video",
"pixel_video_sw",
+ "pixel_vpn",
"pixel_watch",
+ "pixel_watch_debug_trace",
"pixel_wifi",
"platform_compat",
"platform_security",
- "pixel_watch_debug_trace",
"pmw",
"power",
"preload_safety",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index fb33cb1ff8c0..286c4cd709b1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -822,7 +822,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void onOpModeChanged(int op, int uid, String packageName, String persistentDeviceId)
throws RemoteException {
- mCallback.opChanged(op, uid, packageName, persistentDeviceId);
+ mCallback.opChanged(op, uid, packageName != null ? packageName : "",
+ Objects.requireNonNull(persistentDeviceId));
}
}
@@ -1059,7 +1060,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (Flags.enableAllSqliteAppopsAccesses()) {
mHistoricalRegistry = new HistoricalRegistrySql(context);
} else {
- mHistoricalRegistry = new HistoricalRegistry(this, context);
+ mHistoricalRegistry = new LegacyHistoricalRegistry(this, context);
}
}
@@ -7011,7 +7012,8 @@ public class AppOpsService extends IAppOpsService.Stub {
mHistoricalRegistry = new HistoricalRegistrySql(
(HistoricalRegistrySql) mHistoricalRegistry);
} else {
- mHistoricalRegistry = new HistoricalRegistry((HistoricalRegistry) mHistoricalRegistry);
+ mHistoricalRegistry = new LegacyHistoricalRegistry(
+ (LegacyHistoricalRegistry) mHistoricalRegistry);
}
mHistoricalRegistry.systemReady(mContext.getContentResolver());
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index c53e4bdc2205..3a8d5835ccb1 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -24,6 +24,7 @@ import android.database.DatabaseErrorHandler;
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteRawStatement;
import android.os.Environment;
@@ -174,11 +175,16 @@ class DiscreteOpsDbHelper extends SQLiteOpenHelper {
if (DEBUG) {
Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
}
- SQLiteDatabase db = getWritableDatabase();
- if (bindArgs == null) {
- db.execSQL(sql);
- } else {
- db.execSQL(sql, bindArgs);
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ if (bindArgs == null) {
+ db.execSQL(sql);
+ } else {
+ db.execSQL(sql, bindArgs);
+ }
+ } catch (SQLiteFullException exception) {
+ Slog.e(LOG_TAG, "Couldn't exec sql command, disk is full. Discrete ops "
+ + "db file size (bytes) : " + getDatabaseFile().length(), exception);
}
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 70b7016fbb90..3867cbe1e98b 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -82,9 +82,8 @@ import java.util.Set;
* INITIALIZATION: We can initialize persistence only after the system is ready
* as we need to check the optional configuration override from the settings
* database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}.
- *
+ * relies on {@link LegacyHistoricalRegistry} for controlling that no calls are allowed until then.
+ * All outside calls are going through {@link LegacyHistoricalRegistry}.
*/
abstract class DiscreteOpsRegistry {
private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index 20706b659ffb..771df19ec906 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -81,7 +81,7 @@ import java.util.Set;
* THREADING AND LOCKING:
* For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
* It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link LegacyHistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
* {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
* {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
* It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
index a8128dd11cfe..f4f5775bbced 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
@@ -128,11 +128,11 @@ import java.util.concurrent.TimeUnit;
*/
// TODO (bug:122218838): Make sure we handle start of epoch time
// TODO (bug:122218838): Validate changed time is handled correctly
-final class HistoricalRegistry implements HistoricalRegistryInterface {
+final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
private static final boolean DEBUG = false;
private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE;
- private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName();
+ private static final String LOG_TAG = LegacyHistoricalRegistry.class.getSimpleName();
private static final String PARAMETER_DELIMITER = ",";
private static final String PARAMETER_ASSIGNMENT = "=";
@@ -200,7 +200,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
private final Context mContext;
- HistoricalRegistry(@NonNull Object lock, Context context) {
+ LegacyHistoricalRegistry(@NonNull Object lock, Context context) {
mInMemoryLock = lock;
mContext = context;
if (Flags.enableSqliteAppopsAccesses()) {
@@ -210,7 +210,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
}
}
- HistoricalRegistry(@NonNull HistoricalRegistry other) {
+ LegacyHistoricalRegistry(@NonNull LegacyHistoricalRegistry other) {
this(other.mInMemoryLock, other.mContext);
mMode = other.mMode;
mBaseSnapshotInterval = other.mBaseSnapshotInterval;
@@ -313,9 +313,9 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
final int mode = AppOpsManager.parseHistoricalMode(modeValue);
final long baseSnapshotInterval = Long.parseLong(baseSnapshotIntervalValue);
final int intervalCompressionMultiplier = Integer.parseInt(intervalMultiplierValue);
- setHistoryParameters(mode, baseSnapshotInterval,intervalCompressionMultiplier);
+ setHistoryParameters(mode, baseSnapshotInterval, intervalCompressionMultiplier);
return;
- } catch (NumberFormatException ignored) {}
+ } catch (NumberFormatException ignored) { }
}
Slog.w(LOG_TAG, "Bad value for" + Settings.Global.APPOP_HISTORY_PARAMETERS
+ "=" + setting + " resetting!");
@@ -805,7 +805,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
final Message message = PooledLambda.obtainMessage(
- HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
+ LegacyHistoricalRegistry::persistPendingHistory, LegacyHistoricalRegistry.this);
message.what = MSG_WRITE_PENDING_HISTORY;
IoThread.getHandler().sendMessage(message);
mPendingWrites.offerFirst(ops);
@@ -813,7 +813,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
private static void makeRelativeToEpochStart(@NonNull HistoricalOps ops, long nowMillis) {
ops.setBeginAndEndTime(nowMillis - ops.getEndTimeMillis(),
- nowMillis- ops.getBeginTimeMillis());
+ nowMillis - ops.getBeginTimeMillis());
}
private void pruneFutureOps(@NonNull List<HistoricalOps> ops) {
@@ -979,7 +979,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
final HistoricalOps readOp = readOps.get(i);
currentOps.merge(readOp);
}
- }
+ }
}
private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
@@ -1125,7 +1125,7 @@ final class HistoricalRegistry implements HistoricalRegistryInterface {
if (existingOpCount > 0) {
// Compute elapsed time
final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
- .getEndTimeMillis();
+ .getEndTimeMillis();
for (int i = 0; i < existingOpCount; i++) {
final HistoricalOps existingOp = existingOps.get(i);
existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8ef79a916530..4b5f06b13885 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1472,8 +1472,8 @@ public class AudioDeviceBroker {
mAudioService.postAccessoryPlugMediaUnmute(device);
}
- /*package*/ int getVssVolumeForDevice(int streamType, int device) {
- return mAudioService.getVssVolumeForDevice(streamType, device);
+ /*package*/ int getVolumeForDeviceIgnoreMute(int streamType, int device) {
+ return mAudioService.getVolumeForDeviceIgnoreMute(streamType, device);
}
/*package*/ int getMaxVssVolumeForStream(int streamType) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 829d9ea7495f..2e6d98485e85 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -2482,7 +2482,7 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceAvailable(
String address, String name, int streamType, String eventSource) {
- final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
+ final int hearingAidVolIndex = mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType,
DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
@@ -2672,7 +2672,7 @@ public class AudioDeviceInventory {
}
final int leAudioVolIndex = (volumeIndex == -1)
- ? mDeviceBroker.getVssVolumeForDevice(streamType, device)
+ ? mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType, device)
: volumeIndex;
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 766456134b20..7f03b2713d9b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -529,7 +529,7 @@ public class AudioService extends IAudioService.Stub
*/
private InputDeviceVolumeHelper mInputDeviceVolumeHelper;
- /*package*/ int getVssVolumeForDevice(int stream, int device) {
+ /*package*/ int getVolumeForDeviceIgnoreMute(int stream, int device) {
final VolumeStreamState streamState = mStreamStates.get(stream);
return streamState != null ? streamState.getIndex(device) : -1;
}
@@ -3336,11 +3336,25 @@ public class AudioService extends IAudioService.Stub
}
private int rescaleIndex(int index, int srcStream, int dstStream) {
- return rescaleIndex(index,
- getVssForStreamOrDefault(srcStream).getMinIndex(),
- getVssForStreamOrDefault(srcStream).getMaxIndex(),
- getVssForStreamOrDefault(dstStream).getMinIndex(),
- getVssForStreamOrDefault(dstStream).getMaxIndex());
+ final VolumeStreamState srcVss = getVssForStreamOrDefault(srcStream);
+ final VolumeStreamState dstVss = getVssForStreamOrDefault(dstStream);
+ int newIndex = rescaleIndex(index, srcVss.getMinIndex(), srcVss.getMaxIndex(),
+ dstVss.getMinIndex(), dstVss.getMaxIndex());
+ // only apply solution for DTMF stream to make sure that it is not muted when
+ // re-aliasing to voice call stream. With ringMyCar flag enabled this will be
+ // automatically solved since we are sending the mute state to APM
+ // TODO(b/402542630): revisit stream aliasing logic with different min index
+ // values / mute states
+ if (!ringMyCar() && dstStream == AudioSystem.STREAM_DTMF
+ && srcStream == AudioSystem.STREAM_VOICE_CALL
+ && srcVss.getMinIndex() > dstVss.getMinIndex()) {
+ newIndex += srcVss.getMinIndex() - dstVss.getMinIndex();
+ if (newIndex > dstVss.getMaxIndex()) {
+ newIndex = dstVss.getMaxIndex();
+ }
+ }
+
+ return newIndex;
}
private int rescaleIndex(int index, int srcMin, int srcMax, int dstMin, int dstMax) {
@@ -5100,7 +5114,7 @@ public class AudioService extends IAudioService.Stub
}
final int device = absVolumeDevices.toArray(new Integer[0])[0].intValue();
- final int index = getStreamVolume(streamType, device);
+ final int index = (getVolumeForDeviceIgnoreMute(streamType, device) + 5) / 10;
if (DEBUG_VOL) {
Slog.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
@@ -15109,6 +15123,61 @@ public class AudioService extends IAudioService.Stub
/**
* @hide
+ * Returns the current audio output device delay value of key in milliseconds.
+ *
+ * In aidl implementation, the param of getParameters is "key" and reply delay of all supported
+ * devices which be composited as "key=device,address,delay|device,address,delay|...".
+ * e.g.
+ * param: additional_output_device_delay=
+ * reply: additional_output_device_delay=2,,0|4,,0|8,,0|128,,0|256,,0|512,,0|1024,,0|8192,,0|
+ * 16384,,0|262144,,0|262145,,0|524288,,0|67108864,,0|134217728,,0|536870912,,0|536870913,,0|
+ * 536870914,,0
+ *
+ * In hidl implementation, the param of getParameters is "key=device,address" and reply a
+ * specific device delay which be composited as "key=device,address,delay".
+ * e.g.
+ * param: additional_output_device_delay=2,
+ * reply: additional_output_device_delay=2,,0
+ *
+ * @param key
+ * @param deviceType
+ * @param address
+ * @return the delay value of key. This is a non-negative number.
+ * {@code 0} is returned if unsupported.
+ */
+ private long getDelayByKeyDevice(@NonNull String key, @NonNull AudioDeviceAttributes device) {
+ long delayMillis = 0;
+
+ try {
+ if (AudioHalVersionInfo.AUDIO_HAL_TYPE_AIDL == getHalVersion().getHalType()) {
+ final String reply = AudioSystem.getParameters(key);
+ final String keyDeviceAddressPrefix =
+ Integer.toUnsignedString(device.getInternalType()) + "," + device.getAddress() +
+ ",";
+ int start = reply.indexOf(keyDeviceAddressPrefix);
+ int end = -1;
+ if (start != -1) {
+ start += keyDeviceAddressPrefix.length();
+ end = reply.indexOf("|", start);
+ delayMillis = Long.parseLong(
+ end == -1 ? reply.substring(start) : reply.substring(start, end));
+ }
+ } else {
+ final String reply = AudioSystem.getParameters(
+ key + "=" + device.getInternalType() + "," + device.getAddress());
+ if (reply.contains(key)) {
+ delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+ }
+ }
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NullPointerException when getting delay for device " + device, e);
+ }
+
+ return delayMillis;
+ }
+
+ /**
+ * @hide
* Returns the current additional audio output device delay in milliseconds.
*
* @param deviceType
@@ -15124,17 +15193,8 @@ public class AudioService extends IAudioService.Stub
device = retrieveBluetoothAddress(device);
final String key = "additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
/**
@@ -15156,17 +15216,8 @@ public class AudioService extends IAudioService.Stub
device = retrieveBluetoothAddress(device);
final String key = "max_additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
@android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 643f3308d8f5..67afff79dffd 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -724,7 +724,7 @@ public class SoundDoseHelper {
int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC);
if (safeDevicesContains(device) && isStreamActive) {
scheduleMusicActiveCheck();
- int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
+ int index = mAudioService.getVolumeForDeviceIgnoreMute(AudioSystem.STREAM_MUSIC,
device);
if (index > safeMediaVolumeIndex(device)) {
// Approximate cumulative active music time
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ac0892b92646..aa985907071f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1113,7 +1113,11 @@ public class Vpn {
}
// Remove always-on VPN if it's not supported.
if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false, null);
+ // Do not remove the always-on setting due to the restricted ability in safe mode.
+ // The always-on VPN can then start after the device reboots to normal mode.
+ if (!mContext.getPackageManager().isSafeMode()) {
+ setAlwaysOnPackage(null, false, null);
+ }
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3aaf4f6fe85a..7450dffc05ab 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,8 +122,8 @@ final class DisplayDeviceInfo {
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
- * Flag: This flag identifies secondary displays that should show system decorations, such as
- * navigation bar, home activity or wallpaper.
+ * Flag: This flag identifies secondary displays that should always show system decorations,
+ * such as navigation bar, home activity or wallpaper.
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
* @hide
*/
@@ -191,6 +191,19 @@ final class DisplayDeviceInfo {
public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 19;
/**
+ * Flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag should not be enabled with any of {@link #FLAG_PRIVATE},
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}, or {@link #FLAG_OWN_CONTENT_ONLY} at the
+ * same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 20;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f73b66c78fce..ce8d8a6db9d3 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.util.IndentingPrintWriter;
+
import java.util.ArrayList;
import java.util.List;
@@ -97,4 +99,14 @@ public class DisplayGroup {
}
return displayIds;
}
+
+ /** Dumps information about the DisplayGroup. */
+ void dumpLocked(IndentingPrintWriter ipw) {
+ final int numDisplays = mDisplays.size();
+ for (int i = 0; i < numDisplays; i++) {
+ LogicalDisplay logicalDisplay = mDisplays.get(i);
+ ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
+ + logicalDisplay.getPrimaryDisplayDeviceLocked());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b714ad2bd9e..2cad7ed2e9e9 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -766,7 +766,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
} else {
- if (!res.getBoolean(R.bool.config_localDisplaysMirrorContent)) {
+ if (shouldOwnContentOnly()) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
@@ -780,6 +780,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // Public display with FLAG_OWN_CONTENT_ONLY disabled is allowed to switch the
+ // content mode.
+ if (mIsFirstDisplay
+ || (!isDisplayPrivate(physicalAddress) && !shouldOwnContentOnly())) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
+
if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
@@ -822,6 +831,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
R.string.display_manager_hdmi_display_name);
}
}
+
mInfo.frameRateOverrides = mFrameRateOverrides;
// The display is trusted since it is created by system.
@@ -1467,6 +1477,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return false;
}
+ private boolean shouldOwnContentOnly() {
+ final Resources res = getOverlayContext().getResources();
+ return !res.getBoolean(R.bool.config_localDisplaysMirrorContent);
+ }
+
private boolean isDisplayStealTopFocusDisabled(DisplayAddress.Physical physicalAddress) {
if (physicalAddress == null) {
return false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b2b9ef17ec8d..0e6870f7ed7d 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -489,6 +489,11 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_STEAL_TOP_FOCUS_DISABLED;
}
+ // Rear display should not be allowed to use the content mode switch.
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0
+ && mDevicePosition != Layout.Display.POSITION_REAR) {
+ mBaseDisplayInfo.flags |= Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
@@ -1155,6 +1160,7 @@ final class LogicalDisplay {
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
+ pw.println("mDisplayGroupId=" + mDisplayGroupId);
pw.println("mDisplayGroupName=" + mDisplayGroupName);
pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f4daf8761e9b..4a4c616b34e3 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -478,6 +478,21 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.decreaseIndent();
ipw.println();
}
+
+ final int displayGroupCount = mDisplayGroups.size();
+ ipw.println();
+ ipw.println("Display Groups: size=" + displayGroupCount);
+ for (int i = 0; i < displayGroupCount; i++) {
+ int groupId = mDisplayGroups.keyAt(i);
+ DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+ ipw.println("Group " + groupId + ":");
+ ipw.increaseIndent();
+ displayGroup.dumpLocked(ipw);
+ ipw.decreaseIndent();
+ ipw.println();
+ }
+
+
mDeviceStateToLayoutMap.dumpLocked(ipw);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b5a9b19bc5c5..60b7fca99e7b 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -76,6 +76,7 @@ import java.util.regex.Pattern;
* <li><code>secure</code>: creates a secure display</li>
* <li><code>own_content_only</code>: only shows this display's own content</li>
* <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>fixed_content_mode</code>: not allowed to switch content mode</li>
* <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
* <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
* <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
@@ -117,6 +118,18 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
"should_show_system_decorations";
+ /**
+ * When this flag is set, the overlay display is not allowed to switch content mode.
+ * Note that it is the opposite of {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH},
+ * because we want overlay displays (such as those used for connected display simulation in
+ * development) to have {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH} enabled by
+ * default without explicitly specifying it.
+ *
+ * @see DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH
+ */
+ private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
+ "fixed_content_mode";
+
// Gravity flags to decide where the overlay should be shown.
private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -384,6 +397,17 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
if (mFlags.mShouldShowSystemDecorations) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ if (!mFlags.mFixedContentMode
+ && !mFlags.mOwnContentOnly
+ && !mFlags.mShouldShowSystemDecorations) {
+ // For overlay displays, if FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS and
+ // FLAG_OWN_CONTENT_ONLY are both disabled,
+ // then FLAG_ALLOWS_CONTENT_MODE_SWITCH should be enabled by default,
+ // unless OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE is set.
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
mInfo.state = mState;
@@ -628,16 +652,21 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
/** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
final boolean mShouldShowSystemDecorations;
+ /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
+ final boolean mFixedContentMode;
+
final int mGravity;
OverlayFlags(
boolean secure,
boolean ownContentOnly,
boolean shouldShowSystemDecorations,
+ boolean fixedContentMode,
int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
+ mFixedContentMode = fixedContentMode;
mGravity = gravity;
}
@@ -647,12 +676,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
false /* secure */,
false /* ownContentOnly */,
false /* shouldShowSystemDecorations */,
+ false /* fixedContentMode */,
Gravity.NO_GRAVITY);
}
boolean secure = false;
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
+ boolean fixedContentMode = false;
int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -661,11 +692,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
ownContentOnly = true;
} else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
shouldShowSystemDecorations = true;
+ } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
+ fixedContentMode = true;
} else {
gravity = parseOverlayGravity(flag);
}
}
- return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
+ return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
+ fixedContentMode, gravity);
}
@Override
@@ -674,6 +708,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
.append("secure=").append(mSecure)
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+ .append(", fixedContentMode=").append(mFixedContentMode)
.append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 902eefa824b5..89679c728127 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -666,6 +666,12 @@ final class WifiDisplayAdapter extends DisplayAdapter {
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
// The display is trusted since it is created by system.
mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // The wifi display is allowed to switch content mode since FLAG_PRIVATE,
+ // FLAG_OWN_CONTENT_ONLY, and FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS are not
+ // enabled in WifiDisplayDevice#getDisplayDeviceInfoLocked().
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
mInfo.displayShape =
DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
}
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 7cc178d5ff6c..f5228df3b8b2 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@ public class DisplayManagerFlags {
Flags::committedStateSeparateEvent
);
+ private final FlagState mSeparateTimeouts = new FlagState(
+ Flags.FLAG_SEPARATE_TIMEOUTS,
+ Flags::separateTimeouts
+ );
+
private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
Flags::delayImplicitRrRegistrationUntilRrAccessed
@@ -608,6 +613,14 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for having a separate timeouts for power groups
+ * is enabled
+ */
+ public boolean isSeparateTimeoutsEnabled() {
+ return mSeparateTimeouts.isEnabled();
+ }
+
+ /**
* @return {@code true} if the flag for only explicit subscription for RR changes is enabled
*/
public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
@@ -671,6 +684,7 @@ public class DisplayManagerFlags {
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
pw.println(" " + mCommittedStateSeparateEvent);
+ pw.println(" " + mSeparateTimeouts);
pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
}
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 a0064a9f5d1d..007646f6a605 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
@@ -509,6 +509,14 @@ flag {
}
}
+
+flag {
+ name: "separate_timeouts"
+ namespace: "lse_desktop_experience"
+ description: "Allow separate timeouts for different power groups"
+ bug: "402356291"
+}
+
flag {
name: "delay_implicit_rr_registration_until_rr_accessed"
namespace: "display_manager"
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index 2751835f9958..50782a2f22c8 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -16,9 +16,11 @@
package com.android.server.display.mode;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.Looper;
+import android.util.LongSparseArray;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayEventReceiver;
@@ -34,72 +36,128 @@ final class ModeChangeObserver {
@SuppressWarnings("unused")
private DisplayEventReceiver mModeChangeListener;
- private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
- private Looper mLooper;
+ private DisplayManager.DisplayListener mDisplayListener;
+ private final LongSparseArray<Set<Integer>> mRejectedModesMap =
+ new LongSparseArray<>();
+ private final LongSparseArray<Integer> mPhysicalIdToLogicalIdMap = new LongSparseArray<>();
+ private final Looper mLooper;
+ private final Handler mHandler;
+ /**
+ * Observer for display mode changes.
+ * This class observes display mode rejections and updates the vote storage
+ * for rejected modes vote accordingly.
+ */
ModeChangeObserver(VotesStorage votesStorage, DisplayModeDirector.Injector injector,
Looper looper) {
mVotesStorage = votesStorage;
mInjector = injector;
mLooper = looper;
+ mHandler = new Handler(mLooper);
}
+ /**
+ * Start observing display mode changes.
+ */
void observe() {
- mModeChangeListener = new DisplayEventReceiver(mLooper) {
+ updatePhysicalIdToLogicalIdMap();
+ mDisplayListener = new DisplayManager.DisplayListener() {
@Override
- public void onModeRejected(long physicalDisplayId, int modeId) {
- Slog.d(TAG, "Mode Rejected event received");
- int displayId = getLogicalDisplayId(physicalDisplayId);
- if (displayId < 0) {
- Slog.e(TAG, "Logical Display Id not found");
+ public void onDisplayAdded(int displayId) {
+ updateVoteForDisplay(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ int oldPhysicalDisplayIdIndex = mPhysicalIdToLogicalIdMap.indexOfValue(displayId);
+ if (oldPhysicalDisplayIdIndex < 0) {
+ Slog.e(TAG, "Removed display not found");
return;
}
- populateRejectedModesListByDisplay(displayId, modeId);
+ long oldPhysicalDisplayId =
+ mPhysicalIdToLogicalIdMap.keyAt(oldPhysicalDisplayIdIndex);
+ mPhysicalIdToLogicalIdMap.delete(oldPhysicalDisplayId);
+ mRejectedModesMap.delete(oldPhysicalDisplayId);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES, null);
}
@Override
- public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
- Slog.d(TAG, "Hotplug event received");
- if (!connected) {
- int displayId = getLogicalDisplayId(physicalDisplayId);
- if (displayId < 0) {
- Slog.e(TAG, "Logical Display Id not found");
- return;
- }
- clearRejectedModesListByDisplay(displayId);
+ public void onDisplayChanged(int displayId) {
+ int oldPhysicalDisplayIdIndex = mPhysicalIdToLogicalIdMap.indexOfValue(displayId);
+ if (oldPhysicalDisplayIdIndex < 0) {
+ Slog.e(TAG, "Changed display not found");
+ return;
+ }
+ long oldPhysicalDisplayId =
+ mPhysicalIdToLogicalIdMap.keyAt(oldPhysicalDisplayIdIndex);
+ mPhysicalIdToLogicalIdMap.delete(oldPhysicalDisplayId);
+
+ updateVoteForDisplay(displayId);
+ }
+ };
+ mInjector.registerDisplayListener(mDisplayListener, mHandler,
+ DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+ | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+ | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+ mModeChangeListener = new DisplayEventReceiver(mLooper) {
+ @Override
+ public void onModeRejected(long physicalDisplayId, int modeId) {
+ Slog.d(TAG, "Mode Rejected event received");
+ updateRejectedModesListByDisplay(physicalDisplayId, modeId);
+ if (mPhysicalIdToLogicalIdMap.indexOfKey(physicalDisplayId) < 0) {
+ Slog.d(TAG, "Rejected Modes Vote will be updated after display is added");
+ return;
}
+ mVotesStorage.updateVote(mPhysicalIdToLogicalIdMap.get(physicalDisplayId),
+ Vote.PRIORITY_REJECTED_MODES,
+ Vote.forRejectedModes(mRejectedModesMap.get(physicalDisplayId)));
}
};
}
- private int getLogicalDisplayId(long rejectedModePhysicalDisplayId) {
+ private void updateVoteForDisplay(int displayId) {
+ Display display = mInjector.getDisplay(displayId);
+ if (display == null) {
+ // We can occasionally get a display added or changed event for a display that was
+ // subsequently removed, which means this returns null. Check this case and bail
+ // out early; if it gets re-attached we will eventually get another call back for it.
+ Slog.e(TAG, "Added or Changed display has disappeared");
+ return;
+ }
+ DisplayAddress address = display.getAddress();
+ if (address instanceof DisplayAddress.Physical physical) {
+ long physicalDisplayId = physical.getPhysicalDisplayId();
+ mPhysicalIdToLogicalIdMap.put(physicalDisplayId, displayId);
+ Set<Integer> modes = mRejectedModesMap.get(physicalDisplayId);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES,
+ modes != null ? Vote.forRejectedModes(modes) : null);
+ }
+ }
+
+ private void updatePhysicalIdToLogicalIdMap() {
Display[] displays = mInjector.getDisplays();
for (Display display : displays) {
+ if (display == null) {
+ continue;
+ }
DisplayAddress address = display.getAddress();
if (address instanceof DisplayAddress.Physical physical) {
- long physicalDisplayId = physical.getPhysicalDisplayId();
- if (physicalDisplayId == rejectedModePhysicalDisplayId) {
- return display.getDisplayId();
- }
+ mPhysicalIdToLogicalIdMap.put(physical.getPhysicalDisplayId(),
+ display.getDisplayId());
}
}
- return -1;
}
- private void populateRejectedModesListByDisplay(int displayId, int rejectedModeId) {
- Set<Integer> alreadyRejectedModes = mRejectedModesByDisplay.get(displayId);
+ private void updateRejectedModesListByDisplay(long rejectedModePhysicalDisplayId,
+ int rejectedModeId) {
+ Set<Integer> alreadyRejectedModes =
+ mRejectedModesMap.get(rejectedModePhysicalDisplayId);
if (alreadyRejectedModes == null) {
alreadyRejectedModes = new HashSet<>();
- mRejectedModesByDisplay.put(displayId, alreadyRejectedModes);
+ mRejectedModesMap.put(rejectedModePhysicalDisplayId,
+ alreadyRejectedModes);
}
alreadyRejectedModes.add(rejectedModeId);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES,
- Vote.forRejectedModes(alreadyRejectedModes));
- }
-
- private void clearRejectedModesListByDisplay(int displayId) {
- mRejectedModesByDisplay.remove(displayId);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES, null);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 23757757e336..fde9165a84c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5669,11 +5669,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
}
- // TODO(b/352228316): Remove it once IMMIProxy is removed.
- InputMethodManagerInternal getLocalService(){
- return mInputMethodManagerInternal;
- }
-
private final class LocalServiceImpl extends InputMethodManagerInternal {
@ImfLockFree
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 2d937bdcc683..6db62c8397f3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -171,7 +171,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
- public boolean isInMessageHistory(HubMessage message) {
+ public boolean isInReliableMessageHistory(HubMessage message) {
+ if (!message.isResponseRequired()) return false;
// Clean up the history
Iterator<Map.Entry<Integer, Long>> iterator =
mRxMessageHistoryMap.entrySet().iterator();
@@ -188,7 +189,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber());
}
- public void addMessageToHistory(HubMessage message) {
+ public void addReliableMessageToHistory(HubMessage message) {
+ if (!message.isResponseRequired()) return;
if (mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber())) {
long value = mRxMessageHistoryMap.get(message.getMessageSequenceNumber());
Log.w(
@@ -623,7 +625,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
return ErrorCode.PERMANENT_ERROR;
}
HubEndpointInfo remote = mSessionMap.get(sessionId).getRemoteEndpointInfo();
- if (mSessionMap.get(sessionId).isInMessageHistory(message)) {
+ if (mSessionMap.get(sessionId).isInReliableMessageHistory(message)) {
Log.e(TAG, "Dropping duplicate message: " + message);
return ErrorCode.TRANSIENT_ERROR;
}
@@ -648,7 +650,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
boolean success =
invokeCallback((consumer) -> consumer.onMessageReceived(sessionId, message));
if (success) {
- mSessionMap.get(sessionId).addMessageToHistory(message);
+ mSessionMap.get(sessionId).addReliableMessageToHistory(message);
}
return success ? ErrorCode.OK : ErrorCode.TRANSIENT_ERROR;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 6a72cc7c7779..7d9f2c29f943 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -24,6 +24,7 @@ import android.hardware.location.GeofenceHardware;
import android.hardware.location.GeofenceHardwareImpl;
import android.location.FusedBatchOptions;
import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
@@ -35,6 +36,8 @@ import android.location.IGnssStatusListener;
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.location.LocationManager;
+import android.location.flags.Flags;
+import android.location.provider.IGnssAssistanceCallback;
import android.location.util.identity.CallerIdentity;
import android.os.BatteryStats;
import android.os.Binder;
@@ -47,12 +50,13 @@ import com.android.internal.app.IBatteryStats;
import com.android.server.FgThread;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.Injector;
+import com.android.server.location.provider.proxy.ProxyGnssAssistanceProvider;
import java.io.FileDescriptor;
import java.util.List;
/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
-public class GnssManagerService {
+public class GnssManagerService implements GnssNative.GnssAssistanceCallbacks {
public static final String TAG = "GnssManager";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
@@ -75,6 +79,8 @@ public class GnssManagerService {
private final GnssMetrics mGnssMetrics;
+ private @Nullable ProxyGnssAssistanceProvider mProxyGnssAssistanceProvider = null;
+
public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
mContext = context.createAttributionContext(ATTRIBUTION_ID);
mGnssNative = gnssNative;
@@ -100,6 +106,16 @@ public class GnssManagerService {
/** Called when system is ready. */
public void onSystemReady() {
mGnssLocationProvider.onSystemReady();
+
+ if (Flags.gnssAssistanceInterfaceJni()) {
+ mProxyGnssAssistanceProvider =
+ ProxyGnssAssistanceProvider.createAndRegister(mContext);
+ if (mProxyGnssAssistanceProvider == null) {
+ Log.e(TAG, "no gnss assistance provider found");
+ } else {
+ mGnssNative.setGnssAssistanceCallbacks(this);
+ }
+ }
}
/** Retrieve the GnssLocationProvider. */
@@ -323,6 +339,29 @@ public class GnssManagerService {
}
}
+ @Override
+ public void onRequestGnssAssistanceInject() {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ if (mProxyGnssAssistanceProvider == null) {
+ Log.e(TAG, "ProxyGnssAssistanceProvider is null");
+ return;
+ }
+ mProxyGnssAssistanceProvider.request(new IGnssAssistanceCallback.Stub() {
+ @Override
+ public void onError() {
+ Log.e(TAG, "GnssAssistanceCallback.onError");
+ }
+
+ @Override
+ public void onResult(GnssAssistance gnssAssistance) {
+ Log.d(TAG, "GnssAssistanceCallback.onResult");
+ mGnssNative.injectGnssAssistance(gnssAssistance);
+ }
+ });
+ }
+
private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
GnssCapabilitiesHalModule(GnssNative gnssNative) {
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index c79a21a7eea8..7b4c56334868 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -23,6 +23,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementsEvent;
@@ -30,6 +31,7 @@ import android.location.GnssNavigationMessage;
import android.location.GnssSignalType;
import android.location.GnssStatus;
import android.location.Location;
+import android.location.flags.Flags;
import android.os.Binder;
import android.os.Handler;
import android.os.SystemClock;
@@ -275,6 +277,12 @@ public class GnssNative {
void onRequestPsdsDownload(int psdsType);
}
+ /** Callbacks for HAL requesting GNSS assistance. */
+ public interface GnssAssistanceCallbacks {
+ /** On request GnssAssistance injection. */
+ void onRequestGnssAssistanceInject();
+ }
+
/** Callbacks for AGPS functionality. */
public interface AGpsCallbacks {
@@ -400,6 +408,7 @@ public class GnssNative {
private TimeCallbacks mTimeCallbacks;
private LocationRequestCallbacks mLocationRequestCallbacks;
private PsdsCallbacks mPsdsCallbacks;
+ private @Nullable GnssAssistanceCallbacks mGnssAssistanceCallbacks;
private AGpsCallbacks mAGpsCallbacks;
private NotificationCallbacks mNotificationCallbacks;
@@ -504,6 +513,16 @@ public class GnssNative {
mNotificationCallbacks = Objects.requireNonNull(callbacks);
}
+ /** Sets GnssAssistanceCallbacks. */
+ public void setGnssAssistanceCallbacks(GnssAssistanceCallbacks callbacks) {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ Preconditions.checkState(!mRegistered);
+ Preconditions.checkState(mGnssAssistanceCallbacks == null);
+ mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
+ }
+
/**
* Registers with the HAL and allows callbacks to begin. Once registered with the native HAL,
* no more callbacks can be added or set. Must only be called once.
@@ -1053,6 +1072,17 @@ public class GnssNative {
mGnssHal.injectNiSuplMessageData(data, length, slotIndex);
}
+ /**
+ * Injects GNSS assistance data into the GNSS HAL.
+ */
+ public void injectGnssAssistance(GnssAssistance assistance) {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ Preconditions.checkState(mRegistered);
+ mGnssHal.injectGnssAssistance(assistance);
+ }
+
@NativeEntryPoint
void reportGnssServiceDied() {
// Not necessary to clear (and restore) binder identity since it runs on another thread.
@@ -1269,6 +1299,15 @@ public class GnssNative {
}
@NativeEntryPoint
+ void gnssAssistanceInjectRequest() {
+ if (!Flags.gnssAssistanceInterfaceJni() || mGnssAssistanceCallbacks == null) {
+ return;
+ }
+ Binder.withCleanCallingIdentity(
+ () -> mGnssAssistanceCallbacks.onRequestGnssAssistanceInject());
+ }
+
+ @NativeEntryPoint
void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
Binder.withCleanCallingIdentity(
@@ -1569,6 +1608,10 @@ public class GnssNative {
protected void injectNiSuplMessageData(byte[] data, int length, int slotIndex) {
native_inject_ni_supl_message_data(data, length, slotIndex);
}
+
+ protected void injectGnssAssistance(GnssAssistance gnssAssistance) {
+ native_inject_gnss_assistance(gnssAssistance);
+ }
}
// basic APIs
@@ -1718,4 +1761,7 @@ public class GnssNative {
private static native boolean native_supports_psds();
private static native void native_inject_psds_data(byte[] data, int length, int psdsType);
+
+ // GNSS Assistance APIs
+ private static native void native_inject_gnss_assistance(GnssAssistance gnssAssistance);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dd52cce9e927..3f2c2228e453 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -46,7 +46,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
public class ConditionProviders extends ManagedServices {
@@ -203,14 +202,7 @@ public class ConditionProviders extends ManagedServices {
@Override
protected void loadDefaultsFromConfig() {
- for (String dndPackage : getDefaultDndAccessPackages(mContext)) {
- addDefaultComponentOrPackage(dndPackage);
- }
- }
-
- static List<String> getDefaultDndAccessPackages(Context context) {
- ArrayList<String> packages = new ArrayList<>();
- String defaultDndAccess = context.getResources().getString(
+ String defaultDndAccess = mContext.getResources().getString(
R.string.config_defaultDndAccessPackages);
if (defaultDndAccess != null) {
String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
@@ -218,10 +210,9 @@ public class ConditionProviders extends ManagedServices {
if (TextUtils.isEmpty(dnds[i])) {
continue;
}
- packages.add(dnds[i]);
+ addDefaultComponentOrPackage(dnds[i]);
}
}
- return packages;
}
@Override
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index dc7129cde5e5..ea7ee4addbe6 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java b/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
deleted file mode 100644
index d65954d11646..000000000000
--- a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-class ZenConfigTrimmer {
-
- private static final String TAG = "ZenConfigTrimmer";
- private static final int MAXIMUM_PARCELED_SIZE = 150_000; // bytes
-
- private final HashSet<String> mTrustedPackages;
-
- ZenConfigTrimmer(Context context) {
- mTrustedPackages = new HashSet<>();
- mTrustedPackages.add(SystemZenRules.PACKAGE_ANDROID);
- mTrustedPackages.addAll(ConditionProviders.getDefaultDndAccessPackages(context));
- }
-
- void trimToMaximumSize(ZenModeConfig config) {
- Map<String, PackageRules> rulesPerPackage = new HashMap<>();
- for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
- PackageRules pkgRules = rulesPerPackage.computeIfAbsent(rule.pkg, PackageRules::new);
- pkgRules.mRules.add(rule);
- }
-
- int totalSize = 0;
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- totalSize += pkgRules.dataSize();
- }
-
- if (totalSize > MAXIMUM_PARCELED_SIZE) {
- List<PackageRules> deletionCandidates = new ArrayList<>();
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- if (!mTrustedPackages.contains(pkgRules.mPkg)) {
- deletionCandidates.add(pkgRules);
- }
- }
- deletionCandidates.sort(Comparator.comparingInt(PackageRules::dataSize).reversed());
-
- evictPackagesFromConfig(config, deletionCandidates, totalSize);
- }
- }
-
- private static void evictPackagesFromConfig(ZenModeConfig config,
- List<PackageRules> deletionCandidates, int currentSize) {
- while (currentSize > MAXIMUM_PARCELED_SIZE && !deletionCandidates.isEmpty()) {
- PackageRules rulesToDelete = deletionCandidates.removeFirst();
- Slog.w(TAG, String.format("Evicting %s zen rules from package '%s' (%s bytes)",
- rulesToDelete.mRules.size(), rulesToDelete.mPkg, rulesToDelete.dataSize()));
-
- for (ZenModeConfig.ZenRule rule : rulesToDelete.mRules) {
- config.automaticRules.remove(rule.id);
- }
-
- currentSize -= rulesToDelete.dataSize();
- }
- }
-
- private static class PackageRules {
- private final String mPkg;
- private final List<ZenModeConfig.ZenRule> mRules;
- private int mParceledSize = -1;
-
- PackageRules(String pkg) {
- mPkg = pkg;
- mRules = new ArrayList<>();
- }
-
- private int dataSize() {
- if (mParceledSize >= 0) {
- return mParceledSize;
- }
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelableList(mRules, 0);
- mParceledSize = parcel.dataSize();
- return mParceledSize;
- } finally {
- parcel.recycle();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8b09c2acb96a..889df512dd60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -48,7 +48,6 @@ import static android.service.notification.ZenModeConfig.isImplicitRuleId;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.notification.Flags.preventZenDeviceEffectsWhileDriving;
-import static com.android.server.notification.Flags.limitZenConfigSize;
import static java.util.Objects.requireNonNull;
@@ -193,7 +192,6 @@ public class ZenModeHelper {
private final ConditionProviders.Config mServiceConfig;
private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
private final ZenModeEventLogger mZenModeEventLogger;
- private final ZenConfigTrimmer mConfigTrimmer;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -228,7 +226,6 @@ public class ZenModeHelper {
mClock = clock;
addCallback(mMetrics);
mAppOps = context.getSystemService(AppOpsManager.class);
- mConfigTrimmer = new ZenConfigTrimmer(mContext);
mDefaultConfig = Flags.modesUi()
? ZenModeConfig.getDefaultConfig()
@@ -2064,20 +2061,20 @@ public class ZenModeHelper {
Log.w(TAG, "Invalid config in setConfigLocked; " + config);
return false;
}
- if (limitZenConfigSize() && (origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP)) {
- mConfigTrimmer.trimToMaximumSize(config);
- }
-
if (config.user != mUser) {
// simply store away for background users
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 346d65a06cc9..76cd5c88b388 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -212,16 +212,6 @@ flag {
}
flag {
- name: "limit_zen_config_size"
- namespace: "systemui"
- description: "Enforce a maximum (serialized) size for the Zen configuration"
- bug: "387498139"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "managed_services_concurrent_multiuser"
namespace: "systemui"
description: "Enables ManagedServices to support Concurrent multi user environment"
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 1fda4782fc86..92e8eb9cd1bb 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -133,6 +133,38 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"file_patterns": [
"core/java/.*Install.*",
@@ -288,6 +320,38 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 092ec8ef4a8a..233b577e1c61 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1078,7 +1078,7 @@ public class UserManagerService extends IUserManager.Stub {
mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
- invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
+ invalidateOwnerNameIfNecessary(getContextResources(), true /* forceUpdate */);
synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
@@ -1184,6 +1184,15 @@ public class UserManagerService extends IUserManager.Stub {
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
}
+ private Resources getSystemResources() {
+ return android.multiuser.Flags.useUnifiedResources()
+ ? getContextResources() : Resources.getSystem();
+ }
+
+ private Resources getContextResources() {
+ return mContext.getResources();
+ }
+
/**
* This method retrieves the {@link UserManagerInternal} only for the purpose of
* PackageManagerService construction.
@@ -1223,7 +1232,7 @@ public class UserManagerService extends IUserManager.Stub {
// Avoid marking pre-created users for removal.
return;
}
- if (ui.lastLoggedInTime == 0 && ui.isGuest() && Resources.getSystem().getBoolean(
+ if (ui.lastLoggedInTime == 0 && ui.isGuest() && getSystemResources().getBoolean(
com.android.internal.R.bool.config_guestUserAutoCreated)) {
// Avoid marking auto-created but not-yet-logged-in guest user for removal. Because a
// new one will be created anyway, and this one doesn't have any personal data in it yet
@@ -1402,7 +1411,7 @@ public class UserManagerService extends IUserManager.Stub {
}
if (isHeadlessSystemUserMode()) {
- final int bootStrategy = mContext.getResources()
+ final int bootStrategy = getContextResources()
.getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
switch (bootStrategy) {
case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
@@ -2983,7 +2992,7 @@ public class UserManagerService extends IUserManager.Stub {
boolean isUserSwitcherEnabled(@UserIdInt int userId) {
boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.USER_SWITCHER_ENABLED,
- Resources.getSystem().getBoolean(com.android.internal
+ getSystemResources().getBoolean(com.android.internal
.R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
return UserManager.supportsMultipleUsers()
@@ -4672,7 +4681,7 @@ public class UserManagerService extends IUserManager.Stub {
UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
if ("Primary".equals(userData.info.name)) {
userData.info.name =
- mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ getContextResources().getString(com.android.internal.R.string.owner_name);
userIdsToWrite.add(userData.info.id);
}
userVersion = 1;
@@ -5002,7 +5011,7 @@ public class UserManagerService extends IUserManager.Stub {
final Bundle restrictions = new Bundle();
try {
- final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+ final String[] defaultFirstUserRestrictions = getContextResources().getStringArray(
com.android.internal.R.array.config_defaultFirstUserRestrictions);
for (String userRestriction : defaultFirstUserRestrictions) {
if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
@@ -6178,7 +6187,7 @@ public class UserManagerService extends IUserManager.Stub {
// If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
- if (Resources.getSystem().getBoolean(
+ if (getSystemResources().getBoolean(
com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) {
android.provider.Settings.Global.putInt(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
@@ -7490,7 +7499,6 @@ public class UserManagerService extends IUserManager.Stub {
final long now = System.currentTimeMillis();
final long nowRealtime = SystemClock.elapsedRealtime();
final StringBuilder sb = new StringBuilder();
- final Resources resources = Resources.getSystem();
if (args != null && args.length > 0) {
switch (args[0]) {
@@ -7573,13 +7581,14 @@ public class UserManagerService extends IUserManager.Stub {
pw.println();
int effectiveMaxSupportedUsers = UserManager.getMaxSupportedUsers();
pw.print(" Max users: " + effectiveMaxSupportedUsers);
- int defaultMaxSupportedUsers = resources.getInteger(R.integer.config_multiuserMaximumUsers);
+ int defaultMaxSupportedUsers = getSystemResources()
+ .getInteger(R.integer.config_multiuserMaximumUsers);
if (effectiveMaxSupportedUsers != defaultMaxSupportedUsers) {
pw.print(" (built-in value: " + defaultMaxSupportedUsers + ")");
}
pw.println(" (limit reached: " + isUserLimitReached() + ")");
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
- pw.println(" All guests ephemeral: " + resources.getBoolean(
+ pw.println(" All guests ephemeral: " + getSystemResources().getBoolean(
com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Force ephemeral users: " + mForceEphemeralUsers);
final boolean isHeadlessSystemUserMode = isHeadlessSystemUserMode();
@@ -7594,7 +7603,7 @@ public class UserManagerService extends IUserManager.Stub {
}
}
if (isHeadlessSystemUserMode) {
- pw.println(" Can switch to headless system user: " + resources
+ pw.println(" Can switch to headless system user: " + getSystemResources()
.getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser));
}
pw.println(" User version: " + mUserVersion);
@@ -8536,8 +8545,7 @@ public class UserManagerService extends IUserManager.Stub {
* or downgraded to non-admin status.
*/
public boolean isMainUserPermanentAdmin() {
- return Resources.getSystem()
- .getBoolean(R.bool.config_isMainUserPermanentAdmin);
+ return getSystemResources().getBoolean(R.bool.config_isMainUserPermanentAdmin);
}
/**
@@ -8546,8 +8554,7 @@ public class UserManagerService extends IUserManager.Stub {
* it is not a full user.
*/
public boolean canSwitchToHeadlessSystemUser() {
- return Resources.getSystem()
- .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
+ return getSystemResources().getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
}
/**
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index c31c287017c3..4ffdb1124571 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -235,15 +235,12 @@ public final class PermissionPolicyService extends SystemService {
this::synchronizeUidPermissionsAndAppOpsAsync);
mAppOpsCallback = new IAppOpsCallback.Stub() {
- public void opChanged(int op, int uid, @Nullable String packageName,
- String persistentDeviceId) {
+ public void opChanged(int op, int uid, String packageName, String persistentDeviceId) {
if (!Objects.equals(persistentDeviceId,
- VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) || uid < 0) {
return;
}
- if (packageName != null) {
- synchronizeUidPermissionsAndAppOpsAsync(uid);
- }
+ synchronizeUidPermissionsAndAppOpsAsync(uid);
resetAppOpPermissionsIfNotRequestedForUidAsync(uid);
}
};
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 102dc071c7b6..417b5c7ae62b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -67,6 +68,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@ public class Notifier {
@Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
private final TrustManager mTrustManager;
private final Vibrator mVibrator;
- private final WakeLockLog mWakeLockLog;
+ @NonNull private final WakeLockLog mPartialWakeLockLog;
+ @NonNull private final WakeLockLog mFullWakeLockLog;
private final DisplayManagerInternal mDisplayManagerInternal;
private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@ public class Notifier {
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mWakeLockLog = mInjector.getWakeLockLog(context);
+ mFullWakeLockLog = mInjector.getWakeLockLog(context);
+ mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockAcquired(tag,
+ getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
}
@@ -473,7 +479,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockReleased(tag,
+ getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
}
@@ -960,11 +967,18 @@ public class Notifier {
* @param pw The stream to print to.
*/
public void dump(PrintWriter pw) {
- if (mWakeLockLog != null) {
- mWakeLockLog.dump(pw);
- }
+ pw.println("Notifier:");
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("Partial Wakelock Log:");
+ mPartialWakeLockLog.dump(ipw);
+
+ ipw.println("");
+ ipw.println("Full Wakelock Log:");
+ mFullWakeLockLog.dump(ipw);
- mWakefulnessSessionObserver.dump(pw);
+ ipw.println("");
+ mWakefulnessSessionObserver.dump(ipw);
}
private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@ public class Notifier {
// Do Nothing
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+
+ getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
+ flags, currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+ getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
+ currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@ public class Notifier {
}
}
+ private @NonNull WakeLockLog getWakeLockLog(int flags) {
+ return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
+ }
+
+ private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
+ int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
+ return attributionUid != -1 ? attributionUid : ownerUid;
+ }
+
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@ public class Notifier {
/**
* Gets the WakeLockLog object
*/
- WakeLockLog getWakeLockLog(Context context);
+ @NonNull WakeLockLog getWakeLockLog(Context context);
/**
* Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@ public class Notifier {
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd454cd61b2c..3eac4b54cd2b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1723,9 +1723,16 @@ public final class PowerManagerService extends SystemService
}
}
- @SuppressWarnings("deprecation")
private static boolean isScreenLock(final WakeLock wakeLock) {
- switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ return isScreenLock(wakeLock.mFlags);
+ }
+
+ /**
+ * Returns if a wakelock flag corresponds to a screen wake lock.
+ */
+ @SuppressWarnings("deprecation")
+ public static boolean isScreenLock(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index eda222e71c9e..7f152d6fc9fa 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -81,11 +81,12 @@ final class WakeLockLog {
private static final int TYPE_ACQUIRE = 0x1;
private static final int TYPE_RELEASE = 0x2;
private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
- private static final int LOG_SIZE = 1024 * 10;
+ private static final int LOG_SIZE = 1024 * 3;
private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
- private static final int TAG_DATABASE_SIZE = 128;
+ private static final int TAG_DATABASE_SIZE = 64;
private static final int TAG_DATABASE_SIZE_MAX = 128;
+ private static final int TAG_DATABASE_STARTING_SIZE = 16;
private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@ final class WakeLockLog {
* @param pw The {@code PrintWriter} to write to.
*/
public void dump(PrintWriter pw) {
- dump(pw, false);
+ dump(pw, /* includeTagDb= */ true);
}
@VisibleForTesting
@@ -1161,15 +1162,16 @@ final class WakeLockLog {
*/
static class TagDatabase {
private final int mInvalidIndex;
- private final TagData[] mArray;
+ private final int mMaxArraySize;
+ private TagData[] mArray;
private Callback mCallback;
TagDatabase(Injector injector) {
- int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
-
- // Largest possible index used as "INVALID", hence the (size - 1) sizing.
- mArray = new TagData[size - 1];
- mInvalidIndex = size - 1;
+ // Largest possible index used as "INVALID", hence the (size - 1) sizing
+ mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
+ int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
+ mArray = new TagData[startingSize];
+ mInvalidIndex = mMaxArraySize;
}
@Override
@@ -1195,8 +1197,10 @@ final class WakeLockLog {
sb.append(", entries: ").append(entries);
sb.append(", Bytes used: ").append(byteEstimate);
if (DEBUG) {
- sb.append(", Avg tag size: ").append(tagSize / tags);
- sb.append("\n ").append(Arrays.toString(mArray));
+ sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
+ for (int i = 0; i < mArray.length; i++) {
+ sb.append("\n [").append(i).append("] ").append(mArray[i]);
+ }
}
return sb.toString();
}
@@ -1284,6 +1288,18 @@ final class WakeLockLog {
return null;
}
+ // We don't have a spot available, see if we can still increase the array size
+ if (firstAvailable == -1) {
+ if (mArray.length < mMaxArraySize) {
+ int oldSize = mArray.length;
+ int newSize = Math.min(oldSize * 2, mMaxArraySize);
+ TagData[] newArray = new TagData[newSize];
+ System.arraycopy(mArray, 0, newArray, 0, oldSize);
+ mArray = newArray;
+ firstAvailable = oldSize;
+ }
+ }
+
// If we need to remove an index, report to listeners that we are removing an index.
boolean useOldest = firstAvailable == -1;
if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@ final class WakeLockLog {
return TAG_DATABASE_SIZE;
}
+ public int getTagDatabaseStartingSize() {
+ return TAG_DATABASE_STARTING_SIZE;
+ }
+
public int getLogSize() {
return LOG_SIZE;
}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ebc50fd85f24..52d4555248ce 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -67,6 +67,10 @@ public class PowerManagerFlags {
new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
Flags::wakelockAttributionViaWorkchain);
+ private final FlagState mDisableFrozenProcessWakelocks =
+ new FlagState(Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS,
+ Flags::disableFrozenProcessWakelocks);
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -121,6 +125,13 @@ public class PowerManagerFlags {
}
/**
+ * @return Whether the feature to disable the frozen process wakelocks is enabled
+ */
+ public boolean isDisableFrozenProcessWakelocksEnabled() {
+ return mDisableFrozenProcessWakelocks.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -132,6 +143,7 @@ public class PowerManagerFlags {
pw.println(" " + mFrameworkWakelockInfo);
pw.println(" " + mMoveWscLoggingToNotifier);
pw.println(" " + mWakelockAttributionViaWorkchain);
+ pw.println(" " + mDisableFrozenProcessWakelocks);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index fefe195dc337..ad8ec0354aa6 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -70,3 +70,10 @@ flag {
description: "Feature flag to move logging of WakelockStateChanged atoms from BatteryStatsImpl to Notifier."
bug: "352602149"
}
+
+flag {
+ name: "disable_frozen_process_wakelocks"
+ namespace: "power"
+ description: "Feature flag to disable/enable wakelocks of a process when it is frozen/unfrozen"
+ bug: "291115867"
+}
diff --git a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
index 799157520ca5..800fc7c25de5 100644
--- a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
+++ b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
@@ -23,6 +23,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.os.Binder;
import android.os.Environment;
import android.util.AtomicFile;
import android.util.Slog;
@@ -119,7 +120,7 @@ class CertificateRevocationStatusManager {
} catch (IOException | JSONException ex) {
Slog.d(TAG, "Fallback to check stored revocation status", ex);
if (ex instanceof IOException && mShouldScheduleJob) {
- scheduleJobToFetchRemoteRevocationJob();
+ Binder.withCleanCallingIdentity(this::scheduleJobToFetchRemoteRevocationJob);
}
try {
revocationList = getStoredRevocationList();
@@ -210,7 +211,7 @@ class CertificateRevocationStatusManager {
return;
}
Slog.d(TAG, "Scheduling job to fetch remote CRL.");
- jobScheduler.schedule(
+ jobScheduler.forNamespace(TAG).schedule(
new JobInfo.Builder(
JOB_ID,
new ComponentName(
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 93fd2768d13e..6872ca9e46ee 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -23,7 +23,9 @@ import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -32,14 +34,21 @@ import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.provider.Settings;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.AdvancedProtectionManager.FeatureId;
+import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType;
import android.security.advancedprotection.IAdvancedProtectionCallback;
import android.security.advancedprotection.IAdvancedProtectionService;
+import android.security.advancedprotection.AdvancedProtectionProtoEnums;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -51,7 +60,9 @@ import com.android.server.security.advancedprotection.features.DisallowInstallUn
import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -61,6 +72,15 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private static final int MODE_CHANGED = 0;
private static final int CALLBACK_ADDED = 1;
+ // Shared preferences keys
+ private static final String PREFERENCE = "advanced_protection_preference";
+ private static final String ENABLED_CHANGE_TIME = "enabled_change_time";
+ private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id";
+ private static final String LAST_DIALOG_TYPE = "last_dialog_type";
+ private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled";
+ private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked";
+ private static final long MILLIS_PER_HOUR = 60 * 60 * 1000;
+
private final Context mContext;
private final Handler mHandler;
private final AdvancedProtectionStore mStore;
@@ -72,6 +92,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
// For tracking only - not called on state change
private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
+ // Used to store logging data
+ private SharedPreferences mSharedPreferences;
+ private boolean mEmitLogs = true;
+
private AdvancedProtectionService(@NonNull Context context) {
super(PermissionEnforcer.fromContext(context));
mContext = context;
@@ -126,6 +150,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
if (provider != null) {
mProviders.add(provider);
}
+
+ mEmitLogs = false;
}
@Override
@@ -178,7 +204,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
if (enabled != isAdvancedProtectionEnabledInternal()) {
mStore.store(enabled);
sendModeChanged(enabled);
- Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
+ logAdvancedProtectionEnabled(enabled);
}
}
} finally {
@@ -188,6 +214,91 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@Override
@EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ logDialogShown_enforcePermission();
+
+ if (!mEmitLogs) {
+ return;
+ }
+
+ int hoursSinceEnabled = hoursSinceLastChange();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED,
+ /*feature_id*/ featureIdToLogEnum(featureId),
+ /*dialogue_type*/ dialogueTypeToLogEnum(type),
+ /*learn_more_clicked*/ learnMoreClicked,
+ /*hours_since_last_change*/ hoursSinceEnabled);
+
+ getSharedPreferences().edit()
+ .putInt(LAST_DIALOG_FEATURE_ID, featureId)
+ .putInt(LAST_DIALOG_TYPE, type)
+ .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked)
+ .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled)
+ .apply();
+ }
+
+ private int featureIdToLogEnum(@FeatureId int featureId) {
+ switch (featureId) {
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP;
+ case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE;
+ default:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN;
+ }
+ }
+
+ private int dialogueTypeToLogEnum(@SupportDialogType int type) {
+ switch (type) {
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING;
+ default:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ }
+ }
+
+ private void logAdvancedProtectionEnabled(boolean enabled) {
+ if (!mEmitLogs) {
+ return;
+ }
+
+ Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled"));
+ SharedPreferences prefs = getSharedPreferences();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED,
+ /*enabled*/ enabled,
+ /*hours_since_enabled*/ hoursSinceLastChange(),
+ /*last_dialog_feature_id*/ featureIdToLogEnum(
+ prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)),
+ /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)),
+ /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false),
+ /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1));
+ prefs.edit()
+ .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis())
+ .apply();
+ }
+
+ private int hoursSinceLastChange() {
+ int hoursSinceEnabled = -1;
+ long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1);
+ if (lastChangeTimeMillis != -1) {
+ hoursSinceEnabled = (int)
+ ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR);
+ }
+ return hoursSinceEnabled;
+ }
+
+ @Override
+ @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
getAdvancedProtectionFeatures_enforcePermission();
List<AdvancedProtectionFeature> features = new ArrayList<>();
@@ -213,6 +324,30 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
.exec(this, in, out, err, args, callback, resultReceiver);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ writer.println("AdvancedProtectionService");
+ writer.println(" isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal());
+ writer.println(" mHooks.size(): " + mHooks.size());
+ writer.println(" mCallbacks.size(): " + mCallbacks.size());
+ writer.println(" mProviders.size(): " + mProviders.size());
+
+ writer.println("Hooks: ");
+ mHooks.stream().forEach(hook -> {
+ writer.println(" " + hook.getClass().getSimpleName() +
+ " available: " + hook.isAvailable());
+ });
+ writer.println(" Providers: ");
+ mProviders.stream().forEach(provider -> {
+ writer.println(" " + provider.getClass().getSimpleName());
+ provider.getFeatures().stream().forEach(feature -> {
+ writer.println(" " + feature.getClass().getSimpleName());
+ });
+ });
+ writer.println(" mSharedPreferences: " + getSharedPreferences().getAll());
+ }
+
void sendModeChanged(boolean enabled) {
Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
.sendToTarget();
@@ -224,6 +359,22 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
.sendToTarget();
}
+ private SharedPreferences getSharedPreferences() {
+ if (mSharedPreferences == null) {
+ initSharedPreferences();
+ }
+ return mSharedPreferences;
+ }
+
+ private synchronized void initSharedPreferences() {
+ if (mSharedPreferences == null) {
+ Context deviceContext = mContext.createDeviceProtectedStorageContext();
+ File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE);
+ mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs,
+ Context.MODE_PRIVATE);
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private final AdvancedProtectionService mService;
diff --git a/services/core/java/com/android/server/sensors/OWNERS b/services/core/java/com/android/server/sensors/OWNERS
new file mode 100644
index 000000000000..6b2247331a33
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/services/sensorservice/OWNERS
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 7ff4ade1101c..9636cc6c77a7 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,7 @@
package com.android.server.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.SensorDirectChannel;
import android.os.ParcelFileDescriptor;
@@ -71,7 +72,7 @@ public abstract class SensorManagerInternal {
/**
* Sends an event for the runtime sensor with the given handle to the framework.
*
- * Only relevant for sending runtime sensor events. @see #createRuntimeSensor.
+ * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p>
*
* @param handle The sensor handle.
* @param type The type of the sensor.
@@ -83,6 +84,21 @@ public abstract class SensorManagerInternal {
@NonNull float[] values);
/**
+ * Sends an additional info event for the runtime sensor with the given handle to the framework.
+ *
+ * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p>
+ *
+ * @param handle The sensor handle.
+ * @param type The type of payload data.
+ * @param serial The sequence number of this frame for this type.
+ * @param timestampNanos Timestamp of the event.
+ * @param values The payload data represented in float values.
+ * @return Whether the event injection was successful.
+ */
+ public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values);
+
+ /**
* Listener for proximity sensor state changes.
*/
public interface ProximityActiveListener {
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 3de191030d71..0d31b22e2020 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -19,6 +19,7 @@ package com.android.server.sensors;
import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.ArrayMap;
@@ -62,6 +63,9 @@ public class SensorService extends SystemService {
private static native void unregisterRuntimeSensorNative(long ptr, int handle);
private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
long timestampNanos, float[] values);
+ private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle,
+ int type, int serial, long timestampNanos, float[] values);
+
public SensorService(Context ctx) {
super(ctx);
@@ -129,6 +133,18 @@ public class SensorService extends SystemService {
}
@Override
+ public boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values) {
+ synchronized (mLock) {
+ if (!mRuntimeSensorHandles.contains(handle)) {
+ return false;
+ }
+ return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial,
+ timestampNanos, values);
+ }
+ }
+
+ @Override
public void addProximityActiveListener(@NonNull Executor executor,
@NonNull ProximityActiveListener listener) {
Objects.requireNonNull(executor, "executor must not be null");
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 27ca83addec8..126b3a7c721f 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -351,18 +351,38 @@ public final class StorageUserConnection {
}
}
+
private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
+ waitForAsyncVoid(asyncCall, /*bindIfNotConnected*/ true,
+ DEFAULT_REMOTE_TIMEOUT_SECONDS);
+ }
+
+ private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall,
+ boolean bindIfNotConnected, int timeoutSeconds) throws Exception {
CompletableFuture<Void> opFuture = new CompletableFuture<>();
RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
- waitForAsync(asyncCall, callback, opFuture, mOutstandingOps,
- DEFAULT_REMOTE_TIMEOUT_SECONDS);
+ waitForAsync(asyncCall, callback, opFuture, mOutstandingOps, bindIfNotConnected,
+ timeoutSeconds);
}
private <T> T waitForAsync(AsyncStorageServiceCall asyncCall, RemoteCallback callback,
CompletableFuture<T> opFuture, ArrayList<CompletableFuture<T>> outstandingOps,
- long timeoutSeconds) throws Exception {
- CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
+ boolean bindIfNotConnected, long timeoutSeconds) throws Exception {
+
+ CompletableFuture<IExternalStorageService> serviceFuture;
+ if (bindIfNotConnected) {
+ serviceFuture = connectIfNeeded();
+ } else {
+ synchronized (mLock) {
+ if (mRemoteFuture == null || mRemoteFuture.getNow(null) == null) {
+ Slog.w(TAG, "Dropping async request as service is not connected"
+ + "and request doesn't require connecting");
+ return null;
+ }
+ serviceFuture = mRemoteFuture;
+ }
+ }
try {
synchronized (mLock) {
@@ -404,7 +424,11 @@ public final class StorageUserConnection {
public void endSession(Session session) throws ExternalStorageServiceException {
try {
waitForAsyncVoid((service, callback) ->
- service.endSession(session.sessionId, callback));
+ service.endSession(session.sessionId, callback),
+ // endSession shouldn't be trying to bind to remote service if the service
+ // isn't connected already as this means that no previous mounting has been
+ // completed.
+ /*bindIfNotConnected*/ false, /*timeoutSeconds*/ 10);
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to end session: " + session, e);
}
@@ -415,7 +439,11 @@ public final class StorageUserConnection {
ExternalStorageServiceException {
try {
waitForAsyncVoid((service, callback) ->
- service.notifyVolumeStateChanged(sessionId, vol, callback));
+ service.notifyVolumeStateChanged(sessionId, vol, callback),
+ // notifyVolumeStateChanged shouldn't be trying to bind to remote service
+ // if the service isn't connected already as this means that
+ // no previous mounting has been completed
+ /*bindIfNotConnected*/ false, /*timeoutSeconds*/ 10);
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to notify volume state changed "
+ "for vol : " + vol, e);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 6e640d890fb8..424439df3c4b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -856,10 +856,14 @@ public class WallpaperCropper {
BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
wallpaperImageSize.set(options.outWidth, options.outHeight);
}
+ boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+ == View.LAYOUT_DIRECTION_RTL;
+ Rect croppedImageBound = getCrop(displaySize, mDefaultDisplayInfo, wallpaperImageSize,
+ getRelativeCropHints(wallpaperData), isRtl);
- double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
- (double) displaySize.y / wallpaperImageSize.y);
- if (maxDisplayToImageRatio > 1.5) {
+ double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
+ (double) displaySize.y / croppedImageBound.height());
+ if (maxDisplayToImageRatio > 1.3) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e91d88901751..df00fa195f03 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -625,7 +625,7 @@ final class ActivityRecord extends WindowToken {
@VisibleForTesting
final TaskFragment.ConfigOverrideHint mResolveConfigHint;
- private final boolean mOptOutEdgeToEdge;
+ final boolean mOptOutEdgeToEdge;
private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
@@ -5590,6 +5590,14 @@ final class ActivityRecord extends WindowToken {
commitVisibility(visible, performLayout, false /* fromTransition */);
}
+ /**
+ * Sets whether safe region bounds are needed for the Activity. This is called from
+ * {@link ActivityStarter} after the source record is created.
+ */
+ void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+ mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(needsSafeRegionBounds);
+ }
+
/** Updates draw state and shows drawn windows. */
void commitFinishDrawing(SurfaceControl.Transaction t) {
boolean committed = false;
@@ -7753,9 +7761,11 @@ final class ActivityRecord extends WindowToken {
final AppCompatAspectRatioPolicy aspectRatioPolicy =
mAppCompatController.getAspectRatioPolicy();
aspectRatioPolicy.reset();
+ final AppCompatSafeRegionPolicy safeRegionPolicy =
+ mAppCompatController.getSafeRegionPolicy();
mAppCompatController.getLetterboxPolicy().resetFixedOrientationLetterboxEligibility();
mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
- isFixedRotationTransforming());
+ isFixedRotationTransforming(), safeRegionPolicy.getLatestSafeRegionBounds());
// Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
// different from windowing mode of the task (PiP) during transition from fullscreen to PiP
@@ -7801,6 +7811,13 @@ final class ActivityRecord extends WindowToken {
}
}
+ // If activity can be letterboxed due to a safe region only, use the safe region bounds
+ // as the resolved bounds. We ignore cases where the letterboxing can happen due to other
+ // app compat conditions and a safe region since the safe region app compat is sandboxed
+ // earlier in TaskFragment.ConfigOverrideHint.resolveTmpOverrides.
+ mAppCompatController.getSafeRegionPolicy().resolveSafeRegionBoundsConfigurationIfNeeded(
+ resolvedConfig, newParentConfiguration);
+
if (isFixedOrientationLetterboxAllowed
|| scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
// In fullscreen, can be letterboxed for aspect ratio.
@@ -7980,7 +7997,7 @@ final class ActivityRecord extends WindowToken {
mAppCompatController.getSizeCompatModePolicy();
final Rect screenResolvedBounds = scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect parentBounds = mResolveConfigHint.mParentBoundsOverride;
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
final boolean isImmersiveMode = isImmersiveMode(parentBounds);
@@ -8167,7 +8184,7 @@ final class ActivityRecord extends WindowToken {
* in this method.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
- final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final Rect parentBounds = mResolveConfigHint.mParentBoundsOverride;
final Rect stableBounds = new Rect();
final Rect outNonDecorBounds = mTmpBounds;
// If orientation is respected when insets are applied, then stableBounds will be empty.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a84a008f66eb..92f51bed419f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2203,6 +2203,9 @@ class ActivityStarter {
? mLaunchParams.mPreferredTaskDisplayArea
: mRootWindowContainer.getDefaultTaskDisplayArea();
mPreferredWindowingMode = mLaunchParams.mWindowingMode;
+ if (mLaunchParams.mNeedsSafeRegionBounds != null) {
+ r.setNeedsSafeRegionBounds(mLaunchParams.mNeedsSafeRegionBounds);
+ }
}
private TaskDisplayArea computeSuggestedLaunchDisplayArea(
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b0563128870a..b7ef1057388c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -36,6 +36,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_VIEW;
@@ -1634,16 +1635,19 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
- private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
+ @VisibleForTesting
+ void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
String reason) {
final Task focusedRootTask = taskDisplayArea.getFocusedRootTask();
if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
- || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) {
+ || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents()
+ && focusedRootTask.getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW)) {
// We move root home task to front when we are on a fullscreen display area and
// caller has requested the home activity to move with it. Or the previous root task
- // is recents.
+ // is recents and we are not on multi-window mode.
+
taskDisplayArea.moveHomeRootTaskToFront(reason);
}
}
@@ -2830,6 +2834,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
"startActivityFromRecents: Task " + taskId + " not found.");
}
+
+ if (task.getRootTask() != null
+ && task.getRootTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ // Don't move home forward if task is in multi window mode
+ moveHomeTaskForward = false;
+ }
+
if (moveHomeTaskForward) {
// We always want to return to the home activity instead of the recents
// activity from whatever is started from the recents activity, so move
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 3535a96d9c45..d6f058a34367 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -240,7 +240,7 @@ class AppCompatAspectRatioPolicy {
final Configuration resolvedConfig = mActivityRecord.getResolvedOverrideConfiguration();
final Rect parentAppBounds =
mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect parentBounds = mActivityRecord.mResolveConfigHint.mParentBoundsOverride;
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// Use tmp bounds to calculate aspect ratio so we can know whether the activity should
// use restricted size (resolved bounds may be the requested override bounds).
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index c479591a5e0d..28f9a33d65d3 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -33,6 +33,8 @@ class AppCompatController {
@NonNull
private final AppCompatAspectRatioPolicy mAspectRatioPolicy;
@NonNull
+ private final AppCompatSafeRegionPolicy mSafeRegionPolicy;
+ @NonNull
private final AppCompatReachabilityPolicy mReachabilityPolicy;
@NonNull
private final DesktopAppCompatAspectRatioPolicy mDesktopAspectRatioPolicy;
@@ -62,6 +64,7 @@ class AppCompatController {
mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
mAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
mTransparentPolicy, mAppCompatOverrides);
+ mSafeRegionPolicy = new AppCompatSafeRegionPolicy(activityRecord, packageManager);
mReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
wmService.mAppCompatConfiguration);
mLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord,
@@ -90,6 +93,11 @@ class AppCompatController {
}
@NonNull
+ AppCompatSafeRegionPolicy getSafeRegionPolicy() {
+ return mSafeRegionPolicy;
+ }
+
+ @NonNull
DesktopAppCompatAspectRatioPolicy getDesktopAspectRatioPolicy() {
return mDesktopAspectRatioPolicy;
}
@@ -163,6 +171,6 @@ class AppCompatController {
getTransparentPolicy().dump(pw, prefix);
getLetterboxPolicy().dump(pw, prefix);
getSizeCompatModePolicy().dump(pw, prefix);
+ getSafeRegionPolicy().dump(pw, prefix);
}
-
}
diff --git a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
new file mode 100644
index 000000000000..959609309da1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Encapsulate app compat policy logic related to a safe region.
+ */
+class AppCompatSafeRegionPolicy {
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ final PackageManager mPackageManager;
+ // Whether the Activity needs to be in the safe region bounds.
+ private boolean mNeedsSafeRegionBounds = false;
+ // Denotes the latest safe region bounds. Can be empty if the activity or the ancestors do
+ // not have any safe region bounds.
+ @NonNull
+ private final Rect mLatestSafeRegionBounds = new Rect();
+ // Whether the activity has allowed safe region letterboxing. This can be set through the
+ // manifest and the default value is true.
+ @NonNull
+ private final BooleanSupplier mAllowSafeRegionLetterboxing;
+
+ AppCompatSafeRegionPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull PackageManager packageManager) {
+ mActivityRecord = activityRecord;
+ mPackageManager = packageManager;
+ mAllowSafeRegionLetterboxing = AppCompatUtils.asLazy(() -> {
+ // Application level property.
+ if (allowSafeRegionLetterboxing(packageManager)) {
+ return true;
+ }
+ // Activity level property.
+ try {
+ return packageManager.getPropertyAsUser(
+ PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+ mActivityRecord.mActivityComponent.getPackageName(),
+ mActivityRecord.mActivityComponent.getClassName(),
+ mActivityRecord.mUserId).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return true;
+ }
+ });
+ }
+
+ private boolean allowSafeRegionLetterboxing(PackageManager pm) {
+ try {
+ return pm.getPropertyAsUser(
+ PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+ mActivityRecord.packageName,
+ /* className */ null,
+ mActivityRecord.mUserId).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return true;
+ }
+ }
+
+ /**
+ * Computes the latest safe region bounds in
+ * {@link ActivityRecord#resolveOverrideConfiguration(Configuration)} since the activity has not
+ * been attached to the parent container when the ActivityRecord is instantiated. Note that the
+ * latest safe region bounds will be empty if activity has not allowed safe region letterboxing.
+ *
+ * @return latest safe region bounds as set on an ancestor window container.
+ */
+ public Rect getLatestSafeRegionBounds() {
+ if (!allowSafeRegionLetterboxing()) {
+ mLatestSafeRegionBounds.setEmpty();
+ return null;
+ }
+ // Get the latest safe region bounds since the bounds could have changed
+ final Rect latestSafeRegionBounds = mActivityRecord.getSafeRegionBounds();
+ if (latestSafeRegionBounds != null) {
+ mLatestSafeRegionBounds.set(latestSafeRegionBounds);
+ } else {
+ mLatestSafeRegionBounds.setEmpty();
+ }
+ return latestSafeRegionBounds;
+ }
+
+ /**
+ * Computes bounds when letterboxing is required only for the safe region bounds if needed.
+ */
+ public void resolveSafeRegionBoundsConfigurationIfNeeded(@NonNull Configuration resolvedConfig,
+ @NonNull Configuration newParentConfig) {
+ if (mLatestSafeRegionBounds.isEmpty()) {
+ return;
+ }
+ // If activity can not be letterboxed for a safe region only or it has not been attached
+ // to a WindowContainer yet.
+ if (!isLetterboxedForSafeRegionOnlyAllowed() || mActivityRecord.getParent() == null) {
+ return;
+ }
+ resolvedConfig.windowConfiguration.setBounds(mLatestSafeRegionBounds);
+ mActivityRecord.computeConfigByResolveHint(resolvedConfig, newParentConfig);
+ }
+
+ /**
+ * Safe region bounds can either be applied along with size compat, fixed orientation or
+ * aspect ratio conditions by sandboxing them to the safe region bounds. Or it can be applied
+ * independently when no other letterboxing condition is triggered. This method helps detecting
+ * the latter case.
+ *
+ * @return {@code true} if this application or activity has allowed safe region letterboxing and
+ * can be letterboxed only due to the safe region being set on the current or ancestor window
+ * container.
+ */
+ boolean isLetterboxedForSafeRegionOnlyAllowed() {
+ return !mActivityRecord.areBoundsLetterboxed() && getNeedsSafeRegionBounds()
+ && getLatestSafeRegionBounds() != null;
+ }
+
+ /**
+ * Set {@code true} if this activity needs to be within the safe region bounds, else false.
+ */
+ public void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+ mNeedsSafeRegionBounds = needsSafeRegionBounds;
+ }
+
+ /**
+ * @return {@code true} if this activity needs to be within the safe region bounds.
+ */
+ public boolean getNeedsSafeRegionBounds() {
+ return mNeedsSafeRegionBounds;
+ }
+
+ /** @see android.view.WindowManager#PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING */
+ boolean allowSafeRegionLetterboxing() {
+ return mAllowSafeRegionLetterboxing.getAsBoolean();
+ }
+
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ if (mNeedsSafeRegionBounds) {
+ pw.println(prefix + " mNeedsSafeRegionBounds=true");
+ }
+ if (isLetterboxedForSafeRegionOnlyAllowed()) {
+ pw.println(prefix + " isLetterboxForSafeRegionOnlyAllowed=true");
+ }
+ if (!allowSafeRegionLetterboxing()) {
+ pw.println(prefix + " allowSafeRegionLetterboxing=false");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index b91a12598e01..f872286726c3 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -216,6 +216,7 @@ final class AppCompatUtils {
AppCompatCameraPolicy.getCameraCompatFreeformMode(top);
appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
.getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task));
+ appCompatTaskInfo.setOptOutEdgeToEdge(top.mOptOutEdgeToEdge);
}
/**
@@ -241,6 +242,10 @@ final class AppCompatUtils {
if (aspectRatioPolicy.isLetterboxedForAspectRatioOnly()) {
return "ASPECT_RATIO";
}
+ if (activityRecord.mAppCompatController.getSafeRegionPolicy()
+ .isLetterboxedForSafeRegionOnlyAllowed()) {
+ return "SAFE_REGION";
+ }
return "UNKNOWN_REASON";
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index c26acec19743..1c2569251581 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -38,6 +38,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;
+import android.window.DesktopExperienceFlags;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
@@ -353,6 +354,13 @@ final class ContentRecorder implements WindowContainerListener {
return;
}
+ // Recording should not be started on displays that are eligible for hosting tasks.
+ // See android.view.Display#canHostTasks().
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
+ && mDisplayContent.mDisplay.canHostTasks()) {
+ return;
+ }
+
if (mContentRecordingSession.isWaitingForConsent() || !isDisplayReadyForMirroring()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
+ "nothing");
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index fcc697242ff6..b8027546fdbd 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -38,7 +38,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.protolog.ProtoLog;
import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;
-import com.android.window.flags.Flags;
import java.util.Arrays;
import java.util.Objects;
@@ -336,7 +335,6 @@ class DeferredDisplayUpdater {
/** Returns {@code true} if the transition will control when to turn on the screen. */
boolean waitForTransition(@NonNull Message screenUnblocker) {
- if (!Flags.waitForTransitionOnDisplaySwitch()) return false;
if (!mShouldWaitForTransitionWhenScreenOn) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 7a959c14fbd2..d9354323ae7c 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -24,6 +24,8 @@ import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx;
+import static com.android.internal.policy.DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds;
import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity;
import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds;
@@ -64,49 +66,61 @@ public final class DesktopModeBoundsCalculator {
*/
static void updateInitialBounds(@NonNull Task task, @Nullable WindowLayout layout,
@Nullable ActivityRecord activity, @Nullable ActivityOptions options,
- @NonNull Rect outBounds, @NonNull Consumer<String> logger) {
+ @NonNull LaunchParamsController.LaunchParams outParams,
+ @NonNull Consumer<String> logger) {
// Use stable frame instead of raw frame to avoid launching freeform windows on top of
// stable insets, which usually are system widgets such as sysbar & navbar.
final Rect stableBounds = new Rect();
task.getDisplayArea().getStableRect(stableBounds);
- // If the options bounds size is flexible, update size with calculated desired size.
+ final boolean hasFullscreenOverride = activity != null
+ && activity.mAppCompatController.getAspectRatioOverrides().hasFullscreenOverride();
+ // If the options bounds size is flexible and no fullscreen override has been applied,
+ // update size with calculated desired size.
final boolean updateOptionBoundsSize = options != null
- && options.getFlexibleLaunchSize();
+ && options.getFlexibleLaunchSize() && !hasFullscreenOverride;
// If cascading is also enabled, the position of the options bounds must be respected
// during the size update.
final boolean shouldRespectOptionPosition =
updateOptionBoundsSize && DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue();
+ final int captionHeight = activity != null && shouldExcludeCaptionFromAppBounds(
+ activity.info, task.isResizeable(), activity.mOptOutEdgeToEdge)
+ ? getDesktopViewAppHeaderHeightPx(activity.mWmService.mContext) : 0;
if (options != null && options.getLaunchBounds() != null
&& !updateOptionBoundsSize) {
- outBounds.set(options.getLaunchBounds());
- logger.accept("inherit-from-options=" + outBounds);
+ outParams.mBounds.set(options.getLaunchBounds());
+ logger.accept("inherit-from-options=" + outParams.mBounds);
} else if (layout != null) {
final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (layout.hasSpecifiedSize()) {
- calculateLayoutBounds(stableBounds, layout, outBounds,
+ calculateLayoutBounds(stableBounds, layout, outParams.mBounds,
calculateIdealSize(stableBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
- applyLayoutGravity(verticalGravity, horizontalGravity, outBounds,
+ applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
stableBounds);
logger.accept("layout specifies sizes, inheriting size and applying gravity");
} else if (verticalGravity > 0 || horizontalGravity > 0) {
- outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
- shouldRespectOptionPosition));
- applyLayoutGravity(verticalGravity, horizontalGravity, outBounds,
+ outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+ shouldRespectOptionPosition, captionHeight));
+ applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
stableBounds);
logger.accept("layout specifies gravity, applying desired bounds and gravity");
logger.accept("respecting option bounds cascaded position="
+ shouldRespectOptionPosition);
}
} else {
- outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
- shouldRespectOptionPosition));
+ outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+ shouldRespectOptionPosition, captionHeight));
logger.accept("layout not specified, applying desired bounds");
logger.accept("respecting option bounds cascaded position="
+ shouldRespectOptionPosition);
}
+ if (updateOptionBoundsSize && captionHeight != 0) {
+ outParams.mAppBounds.set(outParams.mBounds);
+ outParams.mAppBounds.top += captionHeight;
+ logger.accept("excluding caption height from app bounds");
+ }
}
/**
@@ -119,7 +133,8 @@ public final class DesktopModeBoundsCalculator {
@NonNull
private static Rect calculateInitialBounds(@NonNull Task task,
@NonNull ActivityRecord activity, @NonNull Rect stableBounds,
- @Nullable ActivityOptions options, boolean shouldRespectOptionPosition
+ @Nullable ActivityOptions options, boolean shouldRespectOptionPosition,
+ int captionHeight
) {
// Display bounds not taking into account insets.
final TaskDisplayArea displayArea = task.getDisplayArea();
@@ -160,7 +175,8 @@ public final class DesktopModeBoundsCalculator {
}
// If activity is unresizeable, regardless of orientation, calculate maximum size
// (within the ideal size) maintaining original aspect ratio.
- yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio);
+ yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio,
+ captionHeight);
}
case ORIENTATION_PORTRAIT -> {
// Device in portrait orientation.
@@ -188,11 +204,12 @@ public final class DesktopModeBoundsCalculator {
// ratio.
yield maximizeSizeGivenAspectRatio(activityOrientation,
new Size(customPortraitWidthForLandscapeApp, idealSize.getHeight()),
- appAspectRatio);
+ appAspectRatio, captionHeight);
}
// For portrait unresizeable activities, calculate maximum size (within the ideal
// size) maintaining original aspect ratio.
- yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio);
+ yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio,
+ captionHeight);
}
default -> idealSize;
};
@@ -232,13 +249,15 @@ public final class DesktopModeBoundsCalculator {
* Calculates the largest size that can fit in a given area while maintaining a specific aspect
* ratio.
*/
+ // TODO(b/400617906): Merge duplicate initial bounds calculations to shared class.
@NonNull
private static Size maximizeSizeGivenAspectRatio(
@ScreenOrientation int orientation,
@NonNull Size targetArea,
- float aspectRatio
+ float aspectRatio,
+ int captionHeight
) {
- final int targetHeight = targetArea.getHeight();
+ final int targetHeight = targetArea.getHeight() - captionHeight;
final int targetWidth = targetArea.getWidth();
final int finalHeight;
final int finalWidth;
@@ -275,7 +294,7 @@ public final class DesktopModeBoundsCalculator {
finalHeight = (int) (finalWidth / aspectRatio);
}
}
- return new Size(finalWidth, finalHeight);
+ return new Size(finalWidth, finalHeight + captionHeight);
}
/**
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index ddcb5eccb1d8..03ba1a51ad7b 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -113,14 +113,23 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
// Copy over any values
outParams.set(currentParams);
- // In Proto2, trampoline task launches of an existing background task can result in the
- // previous windowing mode to be restored even if the desktop mode state has changed.
- // Let task launches inherit the windowing mode from the source task if available, which
- // should have the desired windowing mode set by WM Shell. See b/286929122.
if (source != null && source.getTask() != null) {
final Task sourceTask = source.getTask();
- outParams.mWindowingMode = sourceTask.getWindowingMode();
- appendLog("inherit-from-source=" + outParams.mWindowingMode);
+ if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
+ && isEnteringDesktopMode(sourceTask, options, currentParams)) {
+ // If trampoline source is not freeform but we are entering or in desktop mode,
+ // ignore the source windowing mode and set the windowing mode to freeform
+ outParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
+ appendLog("freeform window mode applied to task trampoline");
+ } else {
+ // In Proto2, trampoline task launches of an existing background task can result in
+ // the previous windowing mode to be restored even if the desktop mode state has
+ // changed. Let task launches inherit the windowing mode from the source task if
+ // available, which should have the desired windowing mode set by WM Shell.
+ // See b/286929122.
+ outParams.mWindowingMode = sourceTask.getWindowingMode();
+ appendLog("inherit-from-source=" + outParams.mWindowingMode);
+ }
}
if (phase == PHASE_WINDOWING_MODE) {
@@ -133,6 +142,11 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
}
if ((options == null || options.getLaunchBounds() == null) && task.hasOverrideBounds()) {
+ if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()) {
+ // We are in desktop, return result done to prevent other modifiers from modifying
+ // exiting task bounds or resolved windowing mode.
+ return RESULT_DONE;
+ }
appendLog("current task has bounds set, not overriding");
return RESULT_SKIP;
}
@@ -150,7 +164,7 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
}
DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options,
- outParams.mBounds, this::appendLog);
+ outParams, this::appendLog);
appendLog("final desktop mode task bounds set to %s", outParams.mBounds);
if (options != null && options.getFlexibleLaunchSize()) {
// Return result done to prevent other modifiers from respecting option bounds and
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 25fdf89afad1..2798e843d6dd 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -211,14 +211,17 @@ class Dimmer {
* child should call setAppearance again to request the Dim to continue.
* If multiple containers call this method, only the changes relative to the topmost will be
* applied.
+ * The creation of the dim layer is delayed if the requested dim and blur are 0.
* @param dimmingContainer Container requesting the dim
* @param alpha Dim amount
* @param blurRadius Blur amount
*/
protected void adjustAppearance(@NonNull WindowState dimmingContainer,
float alpha, int blurRadius) {
- final DimState d = obtainDimState(dimmingContainer);
- d.prepareLookChange(alpha, blurRadius);
+ if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
+ final DimState d = obtainDimState(dimmingContainer);
+ d.prepareLookChange(alpha, blurRadius);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 42b63d125d6b..59a042981375 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,6 +41,7 @@ import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.util.TypedValue.COMPLEX_UNIT_MASK;
import static android.util.TypedValue.COMPLEX_UNIT_SHIFT;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -2161,7 +2162,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishAsyncRotationIfPossible() {
final AsyncRotationController controller = mAsyncRotationController;
- if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
+ if (controller != null) {
controller.completeAll();
mAsyncRotationController = null;
}
@@ -2238,11 +2239,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private void applyRotation(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
- final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
- final boolean rotateSeamlessly =
- mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
- final Transaction transaction =
- shellTransitions ? getSyncTransaction() : getPendingTransaction();
+
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2250,25 +2247,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// #computeScreenConfiguration() later.
updateDisplayAndOrientation(null /* outConfig */);
- if (!shellTransitions) {
- forAllWindows(w -> {
- w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- }, true /* traverseTopToBottom */);
- mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
- if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
- // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
- mDisplayRotation.cancelSeamlessRotation();
- }
- }
+ // Before setDisplayProjection is applied by the start transaction of transition,
+ // set the transform hint to avoid using surface in old rotation.
+ setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
+ // The sync transaction should already contains setDisplayProjection, so unset the
+ // hint to restore the natural state when the transaction is applied.
+ getSyncTransaction().unsetFixedTransformHint(mSurfaceControl);
- if (shellTransitions) {
- // Before setDisplayProjection is applied by the start transaction of transition,
- // set the transform hint to avoid using surface in old rotation.
- setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
- // The sync transaction should already contains setDisplayProjection, so unset the
- // hint to restore the natural state when the transaction is applied.
- transaction.unsetFixedTransformHint(mSurfaceControl);
- }
scheduleAnimation();
mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
@@ -2870,8 +2855,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
- && !mFixedRotationLaunchingApp.isVisible()
- && !mDisplayRotation.isRotatingSeamlessly()) {
+ && !mFixedRotationLaunchingApp.isVisible()) {
clearFixedRotationLaunchingApp();
}
// If there won't be a transition to notify the launch is done, then it should be ready to
@@ -3283,41 +3267,40 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/* inTopology= */ shouldShowContent);
}
- /**
- * Whether the display is allowed to switch the content mode between extended and mirroring.
- * If the content mode is extended, the display will start home activity and show system
- * decorations, such as wallpapaer, status bar and navigation bar.
- * If the content mode is mirroring, the display will not show home activity or system
- * decorations.
- * The content mode is switched when {@link Display#canHostTasks()} changes.
- *
- * Note that we only allow displays that are able to show system decorations to use the content
- * mode switch; however, not all displays that are able to show system decorations are allowed
- * to use the content mode switch.
- */
- boolean allowContentModeSwitch() {
- // The default display should always show system decorations.
- if (isDefaultDisplay) {
+ /**
+ * Whether the display is allowed to switch the content mode between extended and mirroring.
+ * If the content mode is extended, the display will start home activity and show system
+ * decorations, such as wallpapaer, status bar and navigation bar.
+ * If the content mode is mirroring, the display will not show home activity or system
+ * decorations.
+ * The content mode is switched when {@link Display#canHostTasks()} changes.
+ *
+ * Note that we only allow displays that are able to show system decorations to use the content
+ * mode switch; however, not all displays that are able to show system decorations are allowed
+ * to use the content mode switch.
+ */
+ boolean allowContentModeSwitch() {
+ if ((mDisplay.getFlags() & FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0) {
return false;
}
- // Private display should never show system decorations.
- if (isPrivate()) {
+ // The default display should always show system decorations.
+ if (isDefaultDisplay) {
return false;
}
- if (shouldNeverShowSystemDecorations()) {
+ // Private or untrusted display should never show system decorations.
+ if (isPrivate() || !isTrusted()) {
return false;
}
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) {
+ if (shouldNeverShowSystemDecorations()) {
return false;
}
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- // Virtual displays cannot add or remove system decorations during their lifecycle.
- if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should always show system
+ // decorations, and should not switch the content mode.
+ if ((mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
return false;
}
@@ -5072,7 +5055,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
+ if (!inTransition()) {
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
@@ -5698,7 +5681,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// This display is configured to show system decorations.
return true;
}
- if (isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
+ if (!DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
+ && isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
// System decorations should not be forced on a rear display due to security
// policies.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 9cf792d82f56..9edbb70c3b74 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -168,19 +168,6 @@ public class DisplayRotation {
private int mDeferredRotationPauseCount;
/**
- * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
- * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
- * so we need to track when this hits zero so we can apply deferred orientation updates.
- */
- private int mSeamlessRotationCount;
-
- /**
- * True in the interval from starting seamless rotation until the last rotated window draws in
- * the new orientation.
- */
- private boolean mRotatingSeamlessly;
-
- /**
* Behavior of rotation suggestions.
*
* @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
@@ -630,15 +617,6 @@ public class DisplayRotation {
return true;
}
- if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
- // The screen rotation animation uses a screenshot to freeze the screen while windows
- // resize underneath. When we are rotating seamlessly, we allow the elements to
- // transition to their rotated state independently and without a freeze required.
- prepareSeamlessRotation();
- } else {
- cancelSeamlessRotation();
- }
-
// Give a remote handler (system ui) some time to reposition things.
startRemoteRotation(oldRotation, mRotation);
@@ -677,42 +655,6 @@ public class DisplayRotation {
}
}
- /**
- * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
- * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
- * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
- * and it doesn't choose seamless rotation.
- */
- void cancelSeamlessRotation() {
- if (!mRotatingSeamlessly) {
- return;
- }
- mDisplayContent.forAllWindows(w -> {
- if (w.mSeamlesslyRotated) {
- w.cancelSeamlessRotation();
- w.mSeamlesslyRotated = false;
- }
- }, true /* traverseTopToBottom */);
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
- }
-
- private void prepareSeamlessRotation() {
- // We are careful to reset this in case a window was removed before it finished
- // seamless rotation.
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = true;
- }
-
- boolean isRotatingSeamlessly() {
- return mRotatingSeamlessly;
- }
-
- boolean hasSeamlessRotatingWindow() {
- return mSeamlessRotationCount > 0;
- }
-
@VisibleForTesting
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
// Display doesn't need to be frozen because application has been started in correct
@@ -750,13 +692,6 @@ public class DisplayRotation {
return false;
}
- // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
- // complete (that is, waiting for windows to redraw). It's tempting to check
- // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
- if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
- return false;
- }
-
return true;
}
@@ -774,28 +709,6 @@ public class DisplayRotation {
return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
}
- void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
- if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
- return;
- }
-
- w.mSeamlesslyRotated = seamlesslyRotated;
- if (seamlesslyRotated) {
- mSeamlessRotationCount++;
- } else {
- mSeamlessRotationCount--;
- }
- if (mSeamlessRotationCount == 0) {
- ProtoLog.i(WM_DEBUG_ORIENTATION,
- "Performing post-rotate rotation after seamless rotation");
- // Finish seamless rotation.
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
-
- updateRotationAndSendNewConfigIfChanged();
- }
- }
-
void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
mFixedToUserRotation = fixedToUserRotation;
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 040bbe46c3aa..234fbd2ea87a 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -82,6 +82,13 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
*/
private boolean mGivenInsetsReady = false;
+ /**
+ * The last state of the windowContainer. This is used to reset server visibility, in case of
+ * the IME (temporarily) redrawing (e.g. during a rotation), to dispatch the control with
+ * leash again after it has finished drawing.
+ */
+ private boolean mLastDrawn = false;
+
ImeInsetsSourceProvider(@NonNull InsetsSource source,
@NonNull InsetsStateController stateController,
@NonNull DisplayContent displayContent) {
@@ -97,6 +104,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
final WindowState ws =
mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending;
+ mLastDrawn = ws != null && ws.isDrawn();
// isLeashReadyForDispatching (used to dispatch the leash of the control) is
// depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
@@ -158,6 +166,35 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
}
}
+ /**
+ * This is used to determine the desired serverVisibility state. For the IME, just having a
+ * window state that would be visible by policy is not enough.
+ */
+ @Override
+ protected boolean isSurfaceVisible() {
+ final boolean isSurfaceVisible = super.isSurfaceVisible();
+ if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ final WindowState windowState = mWindowContainer.asWindowState();
+ if (mControl != null && windowState != null) {
+ final boolean isDrawn = windowState.isDrawn();
+ if (!isServerVisible() && isSurfaceVisible) {
+ // In case the IME becomes visible, we need to check if it is already drawn and
+ // does not have given insets pending. If it's not yet drawn, we do not set
+ // server visibility
+ return isDrawn && !windowState.mGivenInsetsPending;
+ } else if (mLastDrawn && !isDrawn) {
+ // If the IME was drawn before, but is not drawn anymore, we need to reset
+ // server visibility, which will also reset {@link
+ // ImeInsetsSourceProvider#mGivenInsetsReady}. Otherwise, the new control
+ // with leash won't be dispatched after the surface has redrawn.
+ return false;
+ }
+ }
+ }
+ return isSurfaceVisible;
+ }
+
+
@Nullable
@Override
InsetsSourceControl getControl(InsetsControlTarget target) {
@@ -779,6 +816,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
pw.print(prefix);
pw.print("mImeShowing=");
pw.print(mImeShowing);
+ pw.print(" mLastDrawn=");
+ pw.print(mLastDrawn);
if (mImeRequester != null) {
pw.print(prefix);
pw.print("showImePostLayout pending for mImeRequester=");
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1b693fc05b21..dcbb7816a0d7 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -176,6 +176,16 @@ class InsetsSourceProvider {
}
/**
+ * @return Whether the current window container has a visible surface.
+ */
+ protected boolean isSurfaceVisible() {
+ final WindowState windowState = mWindowContainer.asWindowState();
+ return windowState != null
+ ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+ : mWindowContainer.isVisibleRequested();
+ }
+
+ /**
* Updates the window container that currently backs this source.
*
* @param windowContainer The window container that links to this source.
@@ -368,20 +378,9 @@ class InsetsSourceProvider {
if (mWindowContainer == null) {
return;
}
- WindowState windowState = mWindowContainer.asWindowState();
- boolean isServerVisible = windowState != null
- ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
- : mWindowContainer.isVisibleRequested();
+ final WindowState windowState = mWindowContainer.asWindowState();
+ final boolean isServerVisible = isSurfaceVisible();
- if (android.view.inputmethod.Flags.refactorInsetsController()) {
- if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible
- && isServerVisible && windowState != null) {
- // in case the IME becomes visible, we need to check if it is already drawn and
- // does not have given insets pending. If it's not yet drawn, we do not set
- // server visibility
- isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending;
- }
- }
final boolean serverVisibleChanged = mServerVisible != isServerVisible;
setServerVisible(isServerVisible);
if (mControl != null && mControlTarget != null) {
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index fa65bda7104d..2406178e46fe 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -26,6 +26,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration.WindowingMode;
@@ -36,6 +37,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
@@ -96,18 +98,18 @@ class LaunchParamsController {
mTmpResult.reset();
final LaunchParamsModifier modifier = mModifiers.get(i);
- switch(modifier.onCalculate(task, layout, activity, source, options, request, phase,
+ switch (modifier.onCalculate(task, layout, activity, source, options, request, phase,
mTmpCurrent, mTmpResult)) {
case RESULT_SKIP:
// Do not apply any results when we are told to skip
continue;
case RESULT_DONE:
// Set result and return immediately.
- result.set(mTmpResult);
+ result.merge(mTmpResult);
return;
case RESULT_CONTINUE:
// Set result and continue
- result.set(mTmpResult);
+ result.merge(mTmpResult);
break;
}
}
@@ -138,6 +140,10 @@ class LaunchParamsController {
mService.deferWindowLayout();
try {
if (task.getRootTask().inMultiWindowMode()) {
+ if (!mTmpParams.mAppBounds.isEmpty()) {
+ task.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds(
+ mTmpParams.mAppBounds);
+ }
task.setBounds(mTmpParams.mBounds);
return true;
}
@@ -168,7 +174,11 @@ class LaunchParamsController {
*/
static class LaunchParams {
/** The bounds within the parent container. */
+ @NonNull
final Rect mBounds = new Rect();
+ /** The bounds within the parent container respecting insets. Usually empty. */
+ @NonNull
+ final Rect mAppBounds = new Rect();
/** The display area the {@link Task} would prefer to be on. */
@Nullable
@@ -178,24 +188,45 @@ class LaunchParamsController {
@WindowingMode
int mWindowingMode;
+ /** Whether the Activity needs the safe region bounds. A {@code null} value means unset. */
+ @Nullable
+ Boolean mNeedsSafeRegionBounds = null;
+
/** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
void reset() {
mBounds.setEmpty();
+ mAppBounds.setEmpty();
mPreferredTaskDisplayArea = null;
mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ mNeedsSafeRegionBounds = null;
}
/** Copies the values set on the passed in {@link LaunchParams}. */
void set(LaunchParams params) {
mBounds.set(params.mBounds);
+ mAppBounds.set(params.mAppBounds);
+ mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
+ mWindowingMode = params.mWindowingMode;
+ mNeedsSafeRegionBounds = params.mNeedsSafeRegionBounds;
+ }
+
+ /** Merges the values set on the passed in {@link LaunchParams}. */
+ void merge(LaunchParams params) {
+ mBounds.set(params.mBounds);
+ mAppBounds.set(params.mAppBounds);
mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
mWindowingMode = params.mWindowingMode;
+ // Only update mNeedsSafeRegionBounds if a modifier updates it by setting a non null
+ // value. Otherwise, carry over from previous modifiers
+ if (params.mNeedsSafeRegionBounds != null) {
+ mNeedsSafeRegionBounds = params.mNeedsSafeRegionBounds;
+ }
}
/** Returns {@code true} if no values have been explicitly set. */
boolean isEmpty() {
- return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
- && mWindowingMode == WINDOWING_MODE_UNDEFINED;
+ return mBounds.isEmpty() && mAppBounds.isEmpty() && mPreferredTaskDisplayArea == null
+ && mWindowingMode == WINDOWING_MODE_UNDEFINED && mNeedsSafeRegionBounds == null;
}
boolean hasWindowingMode() {
@@ -215,15 +246,20 @@ class LaunchParamsController {
if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
if (mWindowingMode != that.mWindowingMode) return false;
- return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
+ if (!mAppBounds.equals(that.mAppBounds)) return false;
+ if (!Objects.equals(mNeedsSafeRegionBounds, that.mNeedsSafeRegionBounds)) return false;
+ return !mBounds.isEmpty() ? mBounds.equals(that.mBounds) : that.mBounds.isEmpty();
}
@Override
public int hashCode() {
- int result = mBounds != null ? mBounds.hashCode() : 0;
+ int result = !mBounds.isEmpty() ? mBounds.hashCode() : 0;
+ result = 31 * result + mAppBounds.hashCode();
result = 31 * result + (mPreferredTaskDisplayArea != null
? mPreferredTaskDisplayArea.hashCode() : 0);
result = 31 * result + mWindowingMode;
+ result = 31 * result + (mNeedsSafeRegionBounds != null
+ ? Boolean.hashCode(mNeedsSafeRegionBounds) : 0);
return result;
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6dd7d35856df..6e59828c8ff2 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -21,20 +21,13 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.PictureInPictureParams;
import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.util.RotationUtils;
import android.util.Slog;
import android.view.IPinnedTaskListener;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
import java.io.PrintWriter;
@@ -71,11 +64,7 @@ class PinnedTaskController {
* based on the new rotation.
*/
private Rect mDestRotatedBounds;
- /**
- * Non-null if the entering PiP task from recents animation will cause display rotation to
- * change. The transaction is based on the old rotation.
- */
- private PictureInPictureSurfaceTransaction mPipTransaction;
+
/** Whether to skip task configuration change once. */
private boolean mFreezingTaskConfig;
/** Defer display orientation change if the PiP task is animating across orientations. */
@@ -212,14 +201,12 @@ class PinnedTaskController {
}
/**
- * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display
- * will be changed. This is only called when finishing recents animation with pending
- * orientation change that will be handled by
- * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}.
+ * Sets a hint if the orientation of display will be changed. This is only called when
+ * finishing recents animation with pending orientation change that will be handled by
+ * {@link DisplayContent.FixedRotationTransitionListener}.
*/
- void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) {
+ void setEnterPipWithRotatedTransientLaunch() {
mFreezingTaskConfig = true;
- mPipTransaction = tx;
}
/** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */
@@ -233,81 +220,6 @@ class PinnedTaskController {
}
/**
- * Resets rotation and applies scale and position to PiP task surface to match the current
- * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
- * receives the callback of fixed rotation completion.
- */
- void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
- int oldRotation, int newRotation) {
- final Rect bounds = mDestRotatedBounds;
- final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
- if (bounds == null && emptyPipPositionTx) {
- return;
- }
- final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task pinnedTask = taskArea.getRootPinnedTask();
- if (pinnedTask == null) {
- return;
- }
-
- mDestRotatedBounds = null;
- mPipTransaction = null;
- final Rect areaBounds = taskArea.getBounds();
- if (!emptyPipPositionTx) {
- // The transaction from recents animation is in old rotation. So the position needs to
- // be rotated.
- float dx = pipTx.mPosition.x;
- float dy = pipTx.mPosition.y;
- final Matrix matrix = pipTx.getMatrix();
- if (pipTx.mRotation == 90) {
- dx = pipTx.mPosition.y;
- dy = areaBounds.right - pipTx.mPosition.x;
- matrix.postRotate(-90);
- } else if (pipTx.mRotation == -90) {
- dx = areaBounds.bottom - pipTx.mPosition.y;
- dy = pipTx.mPosition.x;
- matrix.postRotate(90);
- }
- matrix.postTranslate(dx, dy);
- final SurfaceControl leash = pinnedTask.getSurfaceControl();
- t.setMatrix(leash, matrix, new float[9]);
- if (pipTx.hasCornerRadiusSet()) {
- t.setCornerRadius(leash, pipTx.mCornerRadius);
- }
- Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
- return;
- }
-
- final PictureInPictureParams params = pinnedTask.getPictureInPictureParams();
- final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
- ? params.getSourceRectHint()
- : null;
- Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
- final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
- // Adjust for display cutout if applicable.
- if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
- if (pinnedTask.getDisplayCutoutInsets() != null) {
- final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
- final Rect displayCutoutInsets = RotationUtils.rotateInsets(
- Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
- sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
- }
- }
- final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
- ? sourceHintRect : areaBounds;
- final int w = contentBounds.width();
- final int h = contentBounds.height();
- final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h;
- final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f);
- final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f);
- final Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop);
- t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]);
- }
-
- /**
* Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that
* there will be a orientation change and a PiP configuration change.
*/
@@ -321,7 +233,6 @@ class PinnedTaskController {
mFreezingTaskConfig = false;
mDeferOrientationChanging = false;
mDestRotatedBounds = null;
- mPipTransaction = null;
}
/**
@@ -381,9 +292,6 @@ class PinnedTaskController {
if (mDestRotatedBounds != null) {
pw.println(prefix + " mPendingBounds=" + mDestRotatedBounds);
}
- if (mPipTransaction != null) {
- pw.println(prefix + " mPipTransaction=" + mPipTransaction);
- }
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 609302ce3f56..39d10624469c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,8 +79,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static java.lang.Integer.MAX_VALUE;
@@ -655,9 +653,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
- if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
- }
if (pendingChanges != 0) {
hasChanges = true;
}
@@ -858,7 +853,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Post these on a handler such that we don't call into power manager service while
// holding the window manager lock to avoid lock contention with power manager lock.
- mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides)
+ // Send a copy of the brightness overrides as they may be cleared before being sent out.
+ mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides.clone())
.sendToTarget();
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
@@ -1024,18 +1020,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return changed;
}
- boolean copyAnimToLayoutParams() {
- boolean doRequest = false;
-
- final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
- if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
- mUpdateRotation = true;
- doRequest = true;
- }
-
- return doRequest;
- }
-
private final class MyHandler extends Handler {
public MyHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 960f5beae2b3..70dabf8d23c0 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2333,15 +2333,24 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Nullable DisplayInfo mTmpOverrideDisplayInfo;
@Nullable AppCompatDisplayInsets mTmpCompatInsets;
@Nullable Rect mParentAppBoundsOverride;
+ @Nullable Rect mParentBoundsOverride;
int mTmpOverrideConfigOrientation;
boolean mUseOverrideInsetsForConfig;
void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
- boolean isFixedRotationTransforming) {
- mParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+ boolean isFixedRotationTransforming, @Nullable Rect safeRegionBounds) {
+ mParentAppBoundsOverride = safeRegionBounds != null ? safeRegionBounds : new Rect(
+ parentConfig.windowConfiguration.getAppBounds());
+ mParentBoundsOverride = safeRegionBounds != null ? safeRegionBounds : new Rect(
+ parentConfig.windowConfiguration.getBounds());
mTmpOverrideConfigOrientation = parentConfig.orientation;
- final Insets insets;
- if (mUseOverrideInsetsForConfig && dc != null
+ Insets insets = Insets.NONE;
+ if (safeRegionBounds != null) {
+ // Modify orientation based on the parent app bounds if safe region bounds are set.
+ mTmpOverrideConfigOrientation =
+ mParentAppBoundsOverride.height() >= mParentAppBoundsOverride.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ } else if (mUseOverrideInsetsForConfig && dc != null
&& !isFloating(parentConfig.windowConfiguration.getWindowingMode())) {
// Insets are decoupled from configuration by default from V+, use legacy
// compatibility behaviour for apps targeting SDK earlier than 35
@@ -2357,10 +2366,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
.getDecorInsetsInfo(rotation, dw, dh);
final Rect stableBounds = decorInsets.mOverrideConfigFrame;
mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height()
- ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
insets = Insets.of(decorInsets.mOverrideNonDecorInsets);
- } else {
- insets = Insets.NONE;
}
mParentAppBoundsOverride.inset(insets);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 803c21ccab6e..30313fc63857 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1249,7 +1249,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Skip dispatching the change for PiP task to avoid its activity drawing for the
// intermediate state which will cause flickering. The final PiP bounds in new
// rotation will be applied by PipTransition.
- ar.mDisplayContent.mPinnedTaskController.setEnterPipTransaction(null);
+ ar.mDisplayContent.mPinnedTaskController.setEnterPipWithRotatedTransientLaunch();
}
return inPip;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 80137a298ac2..3f2b40c1d7c9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -65,9 +65,6 @@ public class WindowAnimator {
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
- int mBulkUpdateParams = 0;
- Object mLastWindowFreezeSource;
-
private boolean mInitialized = false;
private Choreographer mChoreographer;
@@ -145,7 +142,6 @@ public class WindowAnimator {
final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
boolean rootAnimating = false;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = 0;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -202,8 +198,7 @@ public class WindowAnimator {
}
final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
- final boolean doRequest = mBulkUpdateParams != 0 && root.copyAnimToLayoutParams();
- if (hasPendingLayoutChanges || doRequest) {
+ if (hasPendingLayoutChanges) {
mService.mWindowPlacerLocked.requestTraversal();
}
@@ -245,7 +240,6 @@ public class WindowAnimator {
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
@@ -265,17 +259,6 @@ public class WindowAnimator {
mRunningExpensiveAnimations = runningExpensiveAnimations;
}
- private static String bulkUpdateParamsToString(int bulkUpdateParams) {
- StringBuilder builder = new StringBuilder(128);
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
- builder.append(" UPDATE_ROTATION");
- }
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
- builder.append(" SET_WALLPAPER_ACTION_PENDING");
- }
- return builder.toString();
- }
-
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
@@ -292,11 +275,6 @@ public class WindowAnimator {
pw.print(prefix); pw.print("mCurrentTime=");
pw.println(TimeUtils.formatUptime(mCurrentTime));
}
- if (mBulkUpdateParams != 0) {
- pw.print(prefix); pw.print("mBulkUpdateParams=0x");
- pw.print(Integer.toHexString(mBulkUpdateParams));
- pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
- }
}
void scheduleAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 772a7fdfc684..5cbba355a06f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -97,6 +97,7 @@ import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.AlwaysTruePredicate;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -161,6 +162,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
protected @InsetsType int mMergedExcludeInsetsTypes = 0;
private @InsetsType int mExcludeInsetsTypes = 0;
+ /**
+ * Bounds for the safe region for this window container which control the
+ * {@link AppCompatSafeRegionPolicy}. These bounds can be passed on to the subtree if the
+ * subtree has no other bounds for the safe region. The value will be null if there are no safe
+ * region bounds for the window container.
+ */
+ @Nullable
+ private Rect mSafeRegionBounds;
+
@Nullable
private ArrayMap<IBinder, DeathRecipient> mInsetsOwnerDeathRecipientMap;
@@ -556,6 +566,38 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mParent != null ? mParent.mMergedExcludeInsetsTypes : 0);
}
+ /**
+ * Returns the safe region bounds on the window container. If the window container has no safe
+ * region bounds set, the safe region bounds as set on the nearest ancestor is returned.
+ */
+ @Nullable
+ Rect getSafeRegionBounds() {
+ if (mSafeRegionBounds != null) {
+ return mSafeRegionBounds;
+ }
+ if (mParent == null) {
+ return null;
+ }
+ return mParent.getSafeRegionBounds();
+ }
+
+ /**
+ * Sets the safe region bounds on the window container. Set bounds to {@code null} to reset.
+ *
+ * @param safeRegionBounds the safe region {@link Rect} that should be set on this
+ * WindowContainer
+ */
+ void setSafeRegionBounds(@Nullable Rect safeRegionBounds) {
+ if (!Flags.safeRegionLetterboxing()) {
+ Slog.i(TAG, "Feature safe region letterboxing is not available");
+ return;
+ }
+ mSafeRegionBounds = safeRegionBounds;
+ // Trigger a config change whenever this method is called since the safe region bounds
+ // can be modified (including a reset).
+ onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ }
+
private void mergeExcludeInsetsTypesAndNotifyInsetsChanged(
@InsetsType int excludeInsetsTypesFromParent) {
final ArraySet<WindowState> changedWindows = new ArraySet<>();
@@ -3230,6 +3272,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mLocalInsetsSources.valueAt(i).dump(childPrefix, pw);
}
}
+ pw.println(prefix + mSafeRegionBounds + " SafeRegionBounds");
}
final void updateSurfacePositionNonOrganized() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c078d67b6cc6..d7626f00bca8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -198,6 +198,8 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputSettings;
@@ -1032,6 +1034,21 @@ public class WindowManagerService extends IWindowManager.Stub
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
+ private DeviceStateManager mDeviceStateManager;
+ private DeviceStateCallback mDeviceStateCallback;
+ private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
+ private DeviceState mCurrentDeviceState;
+ @Override
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
+ mCurrentDeviceState = state;
+ }
+
+ boolean isInRearDisplayOuterDefaultState() {
+ return mCurrentDeviceState != null && mCurrentDeviceState
+ .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+ }
+ }
+
private float mWindowAnimationScaleSetting = 1.0f;
private float mTransitionAnimationScaleSetting = 1.0f;
private float mAnimatorDurationScaleSetting = 1.0f;
@@ -1317,6 +1334,10 @@ public class WindowManagerService extends IWindowManager.Stub
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+ mDeviceStateCallback = new DeviceStateCallback();
+ mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);
+
if (mPowerManagerInternal != null) {
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@@ -2132,7 +2153,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
final DisplayContent dc = win.getDisplayContent();
- dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */);
win.resetAppOpsState();
@@ -7594,6 +7614,26 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public boolean isEligibleForDesktopMode(int displayId) {
+ if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "isEligibleForDesktopMode()")) {
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
+
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ ProtoLog.e(WM_ERROR, "Attempted to check isEligibleForDesktopMode() "
+ + "for a display that does not exist: %d", displayId);
+ return false;
+ }
+ if (!displayContent.isSystemDecorationsSupported()) {
+ return false;
+ }
+ return displayContent.isDefaultDisplay || displayContent.allowContentModeSwitch();
+ }
+ }
+
+ @Override
public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
@@ -8950,6 +8990,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
+ final Display[] rearDisplays = mDisplayManager
+ .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+ if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
+ // Do not change display focus to the inner display if we're in this mode. Note that
+ // in this mode, the inner display is configured as a rear display.
+ Slog.w(TAG, "Ignoring focus change because device is in RDM.");
+ return;
+ }
+ }
+
clearPointerDownOutsideFocusRunnable();
final InputTarget focusedInputTarget = mFocusedInputTarget;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a012ec137892..c19fa8c03e0a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -78,6 +78,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
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;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
import static android.window.WindowContainerTransaction.HierarchyOp.REACHABILITY_EVENT_X;
import static android.window.WindowContainerTransaction.HierarchyOp.REACHABILITY_EVENT_Y;
@@ -472,35 +473,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
transition.setAllReady();
}
- // TODO(b/365884835): remove this method and callers.
- @Override
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull IWindowContainerTransactionCallback callback,
- @NonNull WindowContainerTransaction t) {
- enforceTaskPermission("startLegacyTransition()");
- final CallerInfo caller = new CallerInfo();
- final long ident = Binder.clearCallingIdentity();
- int syncId;
- try {
- synchronized (mGlobalLock) {
- if (type < 0) {
- throw new IllegalArgumentException("Can't create transition with no type");
- }
- if (mTransitionController.getTransitionPlayer() != null) {
- throw new IllegalArgumentException("Can't use legacy transitions in"
- + " when shell transitions are enabled.");
- }
- syncId = startSyncWithOrganizer(callback);
- applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
- caller);
- setSyncReady(syncId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return syncId;
- }
-
@Override
public void finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
@@ -1544,6 +1516,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
container.setExcludeInsetsTypes(hop.getExcludeInsetsTypes());
break;
}
+ case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS: {
+ final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+ if (container == null || !container.isAttached()) {
+ Slog.e(TAG,
+ "Attempt to operate on unknown or detached container: " + container);
+ break;
+ }
+ if (chain.mTransition != null) {
+ chain.mTransition.collect(container);
+ }
+ container.setSafeRegionBounds(hop.getSafeRegionBounds());
+ effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+ }
}
return effects;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ce91fc5baba1..a270af56cbcd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -100,7 +100,6 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
-import static com.android.input.flags.Flags.removeInputChannelFromWindowstate;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
@@ -170,7 +169,6 @@ import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
-import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -232,7 +230,6 @@ import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
-import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewDebug;
@@ -400,7 +397,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* rotation.
*/
final boolean mForceSeamlesslyRotate;
- SeamlessRotator mPendingSeamlessRotate;
private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -594,13 +590,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** The time when the window was last requested to redraw for orientation change. */
private long mOrientationChangeRedrawRequestTime;
- /**
- * The orientation during the last visible call to relayout. If our
- * current orientation is different, the window can't be ready
- * to be shown.
- */
- int mLastVisibleLayoutRotation = -1;
-
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -613,10 +602,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Input channel and input window handle used by the input dispatcher.
final InputWindowHandleWrapper mInputWindowHandle;
- /**
- * Only populated if flag REMOVE_INPUT_CHANNEL_FROM_WINDOWSTATE is disabled.
- */
- private InputChannel mInputChannel;
/**
* The token will be assigned to {@link InputWindowHandle#token} if this window can receive
@@ -660,15 +645,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mIsSurfacePositionPaused;
/**
- * During seamless rotation we have two phases, first the old window contents
- * are rotated to look as if they didn't move in the new coordinate system. Then we
- * have to freeze updates to this layer (to preserve the transformation) until
- * the resize actually occurs. This is true from when the transformation is set
- * and false until the transaction to resize is sent.
- */
- boolean mSeamlesslyRotated = false;
-
- /**
* Whether the IME insets have been consumed. If {@code true}, this window won't be able to
* receive visible IME insets; {@code false}, otherwise.
*/
@@ -783,11 +759,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
private boolean mInsetsAnimationRunning;
- private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
- finishSeamlessRotation(t);
- updateSurfacePosition(t);
- };
-
private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
// Only apply the position to the surface when there's no leash created.
if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
@@ -904,69 +875,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return visible && mFrozenInsetsState == null;
}
- void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
- @Rotation int rotation, boolean requested) {
- // Invisible windows and the wallpaper do not participate in the seamless rotation animation
- if (!isVisibleNow() || mIsWallpaper) {
- return;
- }
-
- if (mToken.hasFixedRotationTransform()) {
- // The transform of its surface is handled by fixed rotation.
- return;
- }
- final Task task = getTask();
- if (task != null && task.inPinnedWindowingMode()) {
- // It is handled by PinnedTaskController. Note that the windowing mode of activity
- // and windows may still be fullscreen.
- return;
- }
-
- if (mPendingSeamlessRotate != null) {
- oldRotation = mPendingSeamlessRotate.getOldRotation();
- }
-
- // Skip performing seamless rotation when the controlled insets is IME with visible state.
- if (mControllableInsetProvider != null
- && mControllableInsetProvider.getSource().getType() == WindowInsets.Type.ime()) {
- return;
- }
-
- if (mForceSeamlesslyRotate || requested) {
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.startSeamlessRotation();
- }
- mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo(),
- false /* applyFixedTransformationHint */);
- // The surface position is going to be unrotated according to the last position.
- // Make sure the source position is up-to-date.
- mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
- mPendingSeamlessRotate.unrotate(transaction, this);
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- true /* seamlesslyRotated */);
- applyWithNextDraw(mSeamlessRotationFinishedConsumer);
- }
- }
-
- void cancelSeamlessRotation() {
- finishSeamlessRotation(getPendingTransaction());
- }
-
- void finishSeamlessRotation(SurfaceControl.Transaction t) {
- if (mPendingSeamlessRotate == null) {
- return;
- }
-
- mPendingSeamlessRotate.finish(t, this);
- mPendingSeamlessRotate = null;
-
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- false /* seamlesslyRotated */);
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.finishSeamlessRotation();
- }
- }
-
List<Rect> getSystemGestureExclusion() {
return mExclusionRects;
}
@@ -1830,12 +1738,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* Input Manager uses when discarding windows from input consideration.
*/
boolean isPotentialDragTarget(boolean targetInterceptsGlobalDrag) {
- if (removeInputChannelFromWindowstate()) {
- return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
- && mInputChannelToken != null && mInputWindowHandle != null;
- }
return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
- && mInputChannel != null && mInputWindowHandle != null;
+ && mInputChannelToken != null && mInputWindowHandle != null;
}
/**
@@ -2185,8 +2089,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !isDragResizing()
&& hasMovementAnimation
- && !mWinAnimator.mLastHidden
- && !mSeamlesslyRotated;
+ && !mWinAnimator.mLastHidden;
}
/**
@@ -2583,25 +2486,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mInputChannelToken != null) {
throw new IllegalStateException("Window already has an input channel token.");
}
- if (removeInputChannelFromWindowstate()) {
- String name = getName();
- InputChannel channel = mWmService.mInputManager.createInputChannel(name);
- mInputChannelToken = channel.getToken();
- mInputWindowHandle.setToken(mInputChannelToken);
- mWmService.mInputToWindowMap.put(mInputChannelToken, this);
- channel.copyTo(outInputChannel);
- channel.dispose();
- return;
- }
- if (mInputChannel != null) {
- throw new IllegalStateException("Window already has an input channel.");
- }
String name = getName();
- mInputChannel = mWmService.mInputManager.createInputChannel(name);
- mInputChannelToken = mInputChannel.getToken();
+ InputChannel channel = mWmService.mInputManager.createInputChannel(name);
+ mInputChannelToken = channel.getToken();
mInputWindowHandle.setToken(mInputChannelToken);
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
- mInputChannel.copyTo(outInputChannel);
+ channel.copyTo(outInputChannel);
+ channel.dispose();
}
/**
@@ -2624,12 +2515,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mInputChannelToken = null;
}
- if (!removeInputChannelFromWindowstate()) {
- if (mInputChannel != null) {
- mInputChannel.dispose();
- mInputChannel = null;
- }
- }
mInputWindowHandle.setToken(null);
}
@@ -3886,14 +3771,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
/**
- * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
+ * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout or
+ * letterboxed for a safe region.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
- * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
+ * AppCompatLetterboxPolicy#shouldShowLetterboxUi} for more context.
*/
boolean areAppWindowBoundsLetterboxed() {
return mActivityRecord != null && !isStartingWindowAssociatedToTask()
- && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
+ && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout()
+ || mActivityRecord.mAppCompatController
+ .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
}
/** Returns {@code true} if the window is letterboxed for the display cutout. */
@@ -4022,7 +3910,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(REMOVED, mRemoved);
proto.write(IS_ON_SCREEN, isOnScreen());
proto.write(IS_VISIBLE, isVisible);
- proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4168,14 +4055,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
- + " seamlesslyRotate: pending=");
- if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.dump(pw);
- } else {
- pw.print("null");
- }
- pw.println();
if (mXOffset != 0 || mYOffset != 0) {
pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4907,8 +4786,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWinAnimator.mEnterAnimationPending = true;
}
- mLastVisibleLayoutRotation = getDisplayContent().getRotation();
-
mWinAnimator.mEnteringAnimation = true;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
@@ -5306,9 +5183,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
- if ((asyncRotationController != null
- && asyncRotationController.hasSeamlessOperation(mToken))
- || mPendingSeamlessRotate != null) {
+ if (asyncRotationController != null
+ && asyncRotationController.hasSeamlessOperation(mToken)) {
// Freeze position while un-rotating the window, so its surface remains at the position
// corresponding to the original rotation.
return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a34b5115faf9..4fb74ef00914 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -42,9 +42,6 @@ class WindowSurfacePlacer {
/** Only do a maximum of 6 repeated layouts. After that quit */
private int mLayoutRepeatCount;
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 1;
-
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
/** The number of layout requests when deferring. */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 66d04df8095b..adfabe1e54fd 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -169,7 +169,7 @@ cc_defaults {
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.gnss-V3-cpp",
+ "android.hardware.gnss-V5-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -204,6 +204,7 @@ cc_defaults {
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend-V1-ndk",
+ "android_location_flags_c_lib",
"server_configurable_flags",
"service.incremental",
],
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a8c49e11e4e9..ec8794f8073f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,9 +671,13 @@ void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph)
return;
}
- // TODO(b/383092013): Add topology validation
const DisplayTopologyGraph displayTopology =
android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+ if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
+ LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+ return;
+ }
+
mInputManager->getDispatcher().setDisplayTopology(displayTopology);
mInputManager->getChoreographer().setDisplayTopology(displayTopology);
}
@@ -2309,13 +2313,6 @@ static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jobject nativeImplObj, j
locationKeyCode);
}
-static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
- const std::shared_ptr<InputChannel>& inputChannel,
- void* data) {
- NativeInputManager* im = static_cast<NativeInputManager*>(data);
- im->removeInputChannel(inputChannel->getConnectionToken());
-}
-
static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2337,8 +2334,6 @@ static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstr
return nullptr;
}
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- handleInputChannelDisposed, im);
return inputChannelObj;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9c033e25c04e..93f6e95b6d5c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,6 +36,7 @@
#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
#include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <android_location_flags.h>
#include <binder/IServiceManager.h>
#include <nativehelper/JNIHelp.h>
#include <pthread.h>
@@ -53,6 +54,8 @@
#include "gnss/Gnss.h"
#include "gnss/GnssAntennaInfo.h"
#include "gnss/GnssAntennaInfoCallback.h"
+#include "gnss/GnssAssistance.h"
+#include "gnss/GnssAssistanceCallback.h"
#include "gnss/GnssBatching.h"
#include "gnss/GnssConfiguration.h"
#include "gnss/GnssDebug.h"
@@ -114,6 +117,7 @@ using android::hardware::gnss::GnssConstellationType;
using android::hardware::gnss::GnssPowerStats;
using android::hardware::gnss::IGnssPowerIndication;
using android::hardware::gnss::IGnssPowerIndicationCallback;
+using android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -140,6 +144,9 @@ std::unique_ptr<android::gnss::GnssPsdsInterface> gnssPsdsIface = nullptr;
std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
nullptr;
+std::unique_ptr<android::gnss::GnssAssistanceInterface> gnssAssistanceIface = nullptr;
+
+namespace location_flags = android::location::flags;
namespace android {
@@ -229,6 +236,9 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
gnss::GnssVisibilityControl_class_init_once(env, clazz);
gnss::MeasurementCorrections_class_init_once(env, clazz);
gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
+ if (location_flags::gnss_assistance_interface_jni()) {
+ gnss::GnssAssistance_class_init_once(env, clazz);
+ }
gnss::Utils_class_init_once(env);
}
@@ -266,7 +276,9 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
gnssBatchingIface = gnssHal->getGnssBatchingInterface();
gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
-
+ if (location_flags::gnss_assistance_interface_jni()) {
+ gnssAssistanceIface = gnssHal->getGnssAssistanceInterface();
+ }
if (mCallbacksObj) {
ALOGE("Callbacks already initialized");
} else {
@@ -355,13 +367,22 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
// Set IGnssPowerIndication.hal callback.
if (gnssPowerIndicationIface != nullptr) {
sp<IGnssPowerIndicationCallback> gnssPowerIndicationCallback =
- new GnssPowerIndicationCallback();
+ sp<GnssPowerIndicationCallback>::make();
auto status = gnssPowerIndicationIface->setCallback(gnssPowerIndicationCallback);
if (!checkAidlStatus(status, "IGnssPowerIndication setCallback() failed.")) {
gnssPowerIndicationIface = nullptr;
}
}
+ // Set IGnssAssistance callback.
+ if (gnssAssistanceIface != nullptr) {
+ sp<IGnssAssistanceCallback> gnssAssistanceCallback =
+ sp<gnss::GnssAssistanceCallback>::make();
+ if (!gnssAssistanceIface->setCallback(gnssAssistanceCallback)) {
+ ALOGI("IGnssAssistanceInterface setCallback() failed");
+ }
+ }
+
return JNI_TRUE;
}
@@ -493,6 +514,15 @@ static void android_location_gnss_hal_GnssNative_inject_psds_data(JNIEnv* env, j
gnssPsdsIface->injectPsdsData(data, length, psdsType);
}
+static void android_location_gnss_hal_GnssNative_inject_gnss_assistance(JNIEnv* env, jclass,
+ jobject gnssAssistanceObj) {
+ if (gnssAssistanceIface == nullptr) {
+ ALOGE("%s: IGnssAssistance interface not available.", __func__);
+ return;
+ }
+ gnssAssistanceIface->injectGnssAssistance(env, gnssAssistanceObj);
+}
+
static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
if (apn == nullptr) {
@@ -937,6 +967,8 @@ static const JNINativeMethod sLocationProviderMethods[] = {
{"native_stop_nmea_message_collection", "()Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
+ {"native_inject_gnss_assistance", "(Landroid/location/GnssAssistance;)V",
+ reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_gnss_assistance)},
};
static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index eb729de6afd4..0bee7181c2d5 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -60,6 +60,8 @@ public:
void unregisterRuntimeSensor(jint handle);
jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
jfloatArray values);
+ jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial,
+ jlong timestamp, jfloatArray values);
private:
sp<SensorService> mService;
@@ -172,9 +174,9 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j
sensors_event_t event{
.version = sizeof(sensors_event_t),
- .timestamp = timestamp,
.sensor = handle,
.type = type,
+ .timestamp = timestamp,
};
int valuesLength = env->GetArrayLength(values);
@@ -234,6 +236,42 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j
return err == OK;
}
+jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type,
+ jint serial, jlong timestamp,
+ jfloatArray values) {
+ if (mService == nullptr) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available.");
+ return false;
+ }
+
+ sensors_event_t event{
+ .version = sizeof(sensors_event_t),
+ .sensor = handle,
+ .type = SENSOR_TYPE_ADDITIONAL_INFO,
+ .timestamp = timestamp,
+ .additional_info =
+ (additional_info_event_t){
+ .type = type,
+ .serial = serial,
+ },
+ };
+
+ if (values != nullptr) {
+ int valuesLength = env->GetArrayLength(values);
+ if (valuesLength > 14) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum.");
+ return false;
+ }
+ if (valuesLength > 0) {
+ jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr);
+ memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float));
+ }
+ }
+
+ status_t err = mService->sendRuntimeSensorEvent(event);
+ return err == OK;
+}
+
NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
JNIEnv* env, jobject listener)
: mListener(env->NewGlobalRef(listener)) {}
@@ -326,6 +364,13 @@ static jboolean sendRuntimeSensorEventNative(JNIEnv* env, jclass, jlong ptr, jin
return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values);
}
+static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle,
+ jint type, jint serial, jlong timestamp,
+ jfloatArray values) {
+ auto* service = reinterpret_cast<NativeSensorService*>(ptr);
+ return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values);
+}
+
static const JNINativeMethod methods[] = {
{"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
reinterpret_cast<void*>(startSensorServiceNative)},
@@ -340,6 +385,8 @@ static const JNINativeMethod methods[] = {
reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
{"sendRuntimeSensorEventNative", "(JIIJ[F)Z",
reinterpret_cast<void*>(sendRuntimeSensorEventNative)},
+ {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z",
+ reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)},
};
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e72259f094bc..562e82f90bfa 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -17,7 +17,6 @@ cc_library_shared {
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
-
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
],
@@ -41,6 +40,8 @@ cc_library_shared {
"GnssMeasurementCallback.cpp",
"GnssNavigationMessage.cpp",
"GnssNavigationMessageCallback.cpp",
+ "GnssAssistance.cpp",
+ "GnssAssistanceCallback.cpp",
"GnssPsds.cpp",
"GnssPsdsCallback.cpp",
"GnssVisibilityControl.cpp",
@@ -61,7 +62,7 @@ cc_defaults {
"libnativehelper",
"libhardware_legacy",
"libutils",
- "android.hardware.gnss-V3-cpp",
+ "android.hardware.gnss-V5-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
index da8928b5f97f..a3fd9aa79cfb 100644
--- a/services/core/jni/gnss/Gnss.cpp
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -765,4 +765,15 @@ sp<hardware::gnss::V1_0::IGnssNi> GnssHal::getGnssNiInterface() {
return nullptr;
}
+std::unique_ptr<GnssAssistanceInterface> GnssHal::getGnssAssistanceInterface() {
+ if (gnssHalAidl != nullptr) {
+ sp<hardware::gnss::gnss_assistance::IGnssAssistanceInterface> gnssAssistance;
+ auto status = gnssHalAidl->getExtensionGnssAssistanceInterface(&gnssAssistance);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssAssistance")) {
+ return std::make_unique<GnssAssistanceInterface>(gnssAssistance);
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
index 458da8a6e514..2b6b7513a231 100644
--- a/services/core/jni/gnss/Gnss.h
+++ b/services/core/jni/gnss/Gnss.h
@@ -34,6 +34,7 @@
#include "AGnss.h"
#include "AGnssRil.h"
#include "GnssAntennaInfo.h"
+#include "GnssAssistance.h"
#include "GnssBatching.h"
#include "GnssCallback.h"
#include "GnssConfiguration.h"
@@ -115,6 +116,7 @@ public:
std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+ std::unique_ptr<GnssAssistanceInterface> getGnssAssistanceInterface();
sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
diff --git a/services/core/jni/gnss/GnssAssistance.cpp b/services/core/jni/gnss/GnssAssistance.cpp
new file mode 100644
index 000000000000..fff396ea126a
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.cpp
@@ -0,0 +1,2047 @@
+/*
+ * 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+
+#define LOG_TAG "GnssAssistanceJni"
+
+#include "GnssAssistance.h"
+
+#include <utils/String16.h>
+
+#include "GnssAssistanceCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using GnssConstellationType = android::hardware::gnss::GnssConstellationType;
+using GnssCorrectionComponent = android::hardware::gnss::gnss_assistance::GnssCorrectionComponent;
+using GnssInterval =
+ android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::GnssInterval;
+using GnssSatelliteAlmanac =
+ android::hardware::gnss::gnss_assistance::GnssAlmanac::GnssSatelliteAlmanac;
+using IonosphericCorrection = android::hardware::gnss::gnss_assistance::IonosphericCorrection;
+using PseudorangeCorrection =
+ android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::PseudorangeCorrection;
+using GalileoSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+ GalileoSatelliteEphemeris::GalileoSatelliteClockModel;
+using GalileoSvHealth =
+ android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris::GalileoSvHealth;
+using GlonassSatelliteAlmanac =
+ android::hardware::gnss::gnss_assistance::GlonassAlmanac::GlonassSatelliteAlmanac;
+using GlonassSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+ GlonassSatelliteEphemeris::GlonassSatelliteClockModel;
+using GlonassSatelliteOrbitModel = android::hardware::gnss::gnss_assistance::
+ GlonassSatelliteEphemeris::GlonassSatelliteOrbitModel;
+using GnssSignalType = hardware::gnss::GnssSignalType;
+using GnssConstellationType = hardware::gnss::GnssConstellationType;
+using BeidouB1CSatelliteOrbitType =
+ android::hardware::gnss::gnss_assistance::AuxiliaryInformation::BeidouB1CSatelliteOrbitType;
+using QzssSatelliteEphemeris = android::hardware::gnss::gnss_assistance::QzssSatelliteEphemeris;
+
+// Implementation of GnssAssistance (AIDL HAL)
+
+namespace {
+jmethodID method_gnssAssistanceGetGpsAssistance;
+jmethodID method_gnssAssistanceGetGlonassAssistance;
+jmethodID method_gnssAssistanceGetGalileoAssistance;
+jmethodID method_gnssAssistanceGetBeidouAssistance;
+jmethodID method_gnssAssistanceGetQzssAssistance;
+
+jmethodID method_listSize;
+jmethodID method_listGet;
+
+jmethodID method_gnssAlmanacGetIssueDateMillis;
+jmethodID method_gnssAlmanacGetIoda;
+jmethodID method_gnssAlmanacGetWeekNumber;
+jmethodID method_gnssAlmanacGetToaSeconds;
+jmethodID method_gnssAlmanacGetSatelliteAlmanacs;
+jmethodID method_gnssAlmanacIsCompleteAlmanacProvided;
+jmethodID method_satelliteAlmanacGetSvid;
+jmethodID method_satelliteAlmanacGetSvHealth;
+jmethodID method_satelliteAlmanacGetAf0;
+jmethodID method_satelliteAlmanacGetAf1;
+jmethodID method_satelliteAlmanacGetEccentricity;
+jmethodID method_satelliteAlmanacGetInclination;
+jmethodID method_satelliteAlmanacGetM0;
+jmethodID method_satelliteAlmanacGetOmega;
+jmethodID method_satelliteAlmanacGetOmega0;
+jmethodID method_satelliteAlmanacGetOmegaDot;
+jmethodID method_satelliteAlmanacGetRootA;
+
+jmethodID method_satelliteEphemerisTimeGetIode;
+jmethodID method_satelliteEphemerisTimeGetToeSeconds;
+jmethodID method_satelliteEphemerisTimeGetWeekNumber;
+
+jmethodID method_keplerianOrbitModelGetDeltaN;
+jmethodID method_keplerianOrbitModelGetEccentricity;
+jmethodID method_keplerianOrbitModelGetI0;
+jmethodID method_keplerianOrbitModelGetIDot;
+jmethodID method_keplerianOrbitModelGetM0;
+jmethodID method_keplerianOrbitModelGetOmega;
+jmethodID method_keplerianOrbitModelGetOmega0;
+jmethodID method_keplerianOrbitModelGetOmegaDot;
+jmethodID method_keplerianOrbitModelGetRootA;
+jmethodID method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation;
+jmethodID method_secondOrderHarmonicPerturbationGetCic;
+jmethodID method_secondOrderHarmonicPerturbationGetCis;
+jmethodID method_secondOrderHarmonicPerturbationGetCrc;
+jmethodID method_secondOrderHarmonicPerturbationGetCrs;
+jmethodID method_secondOrderHarmonicPerturbationGetCuc;
+jmethodID method_secondOrderHarmonicPerturbationGetCus;
+
+jmethodID method_klobucharIonosphericModelGetAlpha0;
+jmethodID method_klobucharIonosphericModelGetAlpha1;
+jmethodID method_klobucharIonosphericModelGetAlpha2;
+jmethodID method_klobucharIonosphericModelGetAlpha3;
+jmethodID method_klobucharIonosphericModelGetBeta0;
+jmethodID method_klobucharIonosphericModelGetBeta1;
+jmethodID method_klobucharIonosphericModelGetBeta2;
+jmethodID method_klobucharIonosphericModelGetBeta3;
+
+jmethodID method_utcModelGetA0;
+jmethodID method_utcModelGetA1;
+jmethodID method_utcModelGetTimeOfWeek;
+jmethodID method_utcModelGetWeekNumber;
+
+jmethodID method_leapSecondsModelGetDayNumberLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetLeapSeconds;
+jmethodID method_leapSecondsModelGetLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetWeekNumberLeapSecondsFuture;
+
+jmethodID method_timeModelsGetTimeOfWeek;
+jmethodID method_timeModelsGetToGnss;
+jmethodID method_timeModelsGetWeekNumber;
+jmethodID method_timeModelsGetA0;
+jmethodID method_timeModelsGetA1;
+
+jmethodID method_realTimeIntegrityModelGetBadSvid;
+jmethodID method_realTimeIntegrityModelGetBadSignalTypes;
+jmethodID method_realTimeIntegrityModelGetStartDateSeconds;
+jmethodID method_realTimeIntegrityModelGetEndDateSeconds;
+jmethodID method_realTimeIntegrityModelGetPublishDateSeconds;
+jmethodID method_realTimeIntegrityModelGetAdvisoryNumber;
+jmethodID method_realTimeIntegrityModelGetAdvisoryType;
+
+jmethodID method_gnssSignalTypeGetConstellationType;
+jmethodID method_gnssSignalTypeGetCarrierFrequencyHz;
+jmethodID method_gnssSignalTypeGetCodeType;
+
+jmethodID method_auxiliaryInformationGetSvid;
+jmethodID method_auxiliaryInformationGetAvailableSignalTypes;
+jmethodID method_auxiliaryInformationGetFrequencyChannelNumber;
+jmethodID method_auxiliaryInformationGetSatType;
+
+jmethodID method_satelliteCorrectionGetSvid;
+jmethodID method_satelliteCorrectionGetIonosphericCorrections;
+jmethodID method_ionosphericCorrectionGetCarrierFrequencyHz;
+jmethodID method_ionosphericCorrectionGetIonosphericCorrection;
+jmethodID method_gnssCorrectionComponentGetPseudorangeCorrection;
+jmethodID method_gnssCorrectionComponentGetSourceKey;
+jmethodID method_gnssCorrectionComponentGetValidityInterval;
+jmethodID method_pseudorangeCorrectionGetCorrectionMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionUncertaintyMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond;
+jmethodID method_gnssIntervalGetStartMillisSinceGpsEpoch;
+jmethodID method_gnssIntervalGetEndMillisSinceGpsEpoch;
+
+jmethodID method_gpsAssistanceGetAlmanac;
+jmethodID method_gpsAssistanceGetIonosphericModel;
+jmethodID method_gpsAssistanceGetUtcModel;
+jmethodID method_gpsAssistanceGetLeapSecondsModel;
+jmethodID method_gpsAssistanceGetTimeModels;
+jmethodID method_gpsAssistanceGetSatelliteEphemeris;
+jmethodID method_gpsAssistanceGetRealTimeIntegrityModels;
+jmethodID method_gpsAssistanceGetSatelliteCorrections;
+jmethodID method_gpsSatelliteEphemerisGetSvid;
+jmethodID method_gpsSatelliteEphemerisGetGpsL2Params;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_gpsL2ParamsGetL2Code;
+jmethodID method_gpsL2ParamsGetL2Flag;
+jmethodID method_gpsSatelliteClockModelGetAf0;
+jmethodID method_gpsSatelliteClockModelGetAf1;
+jmethodID method_gpsSatelliteClockModelGetAf2;
+jmethodID method_gpsSatelliteClockModelGetTgd;
+jmethodID method_gpsSatelliteClockModelGetIodc;
+jmethodID method_gpsSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_gpsSatelliteHealthGetFitInt;
+jmethodID method_gpsSatelliteHealthGetSvAccur;
+jmethodID method_gpsSatelliteHealthGetSvHealth;
+
+jmethodID method_beidouAssistanceGetAlmanac;
+jmethodID method_beidouAssistanceGetIonosphericModel;
+jmethodID method_beidouAssistanceGetUtcModel;
+jmethodID method_beidouAssistanceGetLeapSecondsModel;
+jmethodID method_beidouAssistanceGetTimeModels;
+jmethodID method_beidouAssistanceGetSatelliteEphemeris;
+jmethodID method_beidouAssistanceGetSatelliteCorrections;
+jmethodID method_beidouAssistanceGetRealTimeIntegrityModels;
+jmethodID method_beidouSatelliteEphemerisGetSvid;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_beidouSatelliteClockModelGetAf0;
+jmethodID method_beidouSatelliteClockModelGetAf1;
+jmethodID method_beidouSatelliteClockModelGetAf2;
+jmethodID method_beidouSatelliteClockModelGetAodc;
+jmethodID method_beidouSatelliteClockModelGetTgd1;
+jmethodID method_beidouSatelliteClockModelGetTgd2;
+jmethodID method_beidouSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_beidouSatelliteHealthGetSatH1;
+jmethodID method_beidouSatelliteHealthGetSvAccur;
+jmethodID method_beidouSatelliteEphemerisTimeGetIode;
+jmethodID method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber;
+jmethodID method_beidouSatelliteEphemerisTimeGetToeSeconds;
+
+jmethodID method_galileoAssistanceGetAlmanac;
+jmethodID method_galileoAssistanceGetIonosphericModel;
+jmethodID method_galileoAssistanceGetUtcModel;
+jmethodID method_galileoAssistanceGetLeapSecondsModel;
+jmethodID method_galileoAssistanceGetTimeModels;
+jmethodID method_galileoAssistanceGetSatelliteEphemeris;
+jmethodID method_galileoAssistanceGetSatelliteCorrections;
+jmethodID method_galileoAssistanceGetRealTimeIntegrityModels;
+jmethodID method_galileoSatelliteEphemerisGetSvid;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteClockModels;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_galileoSatelliteClockModelGetAf0;
+jmethodID method_galileoSatelliteClockModelGetAf1;
+jmethodID method_galileoSatelliteClockModelGetAf2;
+jmethodID method_galileoSatelliteClockModelGetBgdSeconds;
+jmethodID method_galileoSatelliteClockModelGetSatelliteClockType;
+jmethodID method_galileoSatelliteClockModelGetSisaMeters;
+jmethodID method_galileoSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_galileoSvHealthGetDataValidityStatusE1b;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5a;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE1b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5a;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5b;
+jmethodID method_galileoIonosphericModelGetAi0;
+jmethodID method_galileoIonosphericModelGetAi1;
+jmethodID method_galileoIonosphericModelGetAi2;
+
+jmethodID method_glonassAssistanceGetAlmanac;
+jmethodID method_glonassAssistanceGetUtcModel;
+jmethodID method_glonassAssistanceGetTimeModels;
+jmethodID method_glonassAssistanceGetSatelliteEphemeris;
+jmethodID method_glonassAssistanceGetSatelliteCorrections;
+jmethodID method_glonassAlmanacGetIssueDateMillis;
+jmethodID method_glonassAlmanacGetSatelliteAlmanacs;
+jmethodID method_glonassSatelliteAlmanacGetDeltaI;
+jmethodID method_glonassSatelliteAlmanacGetDeltaT;
+jmethodID method_glonassSatelliteAlmanacGetDeltaTDot;
+jmethodID method_glonassSatelliteAlmanacGetEccentricity;
+jmethodID method_glonassSatelliteAlmanacGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteAlmanacGetLambda;
+jmethodID method_glonassSatelliteAlmanacGetOmega;
+jmethodID method_glonassSatelliteAlmanacGetSlotNumber;
+jmethodID method_glonassSatelliteAlmanacGetHealthState;
+jmethodID method_glonassSatelliteAlmanacGetTLambda;
+jmethodID method_glonassSatelliteAlmanacGetTau;
+jmethodID method_glonassSatelliteAlmanacGetIsGlonassM;
+jmethodID method_glonassSatelliteAlmanacGetCalendarDayNumber;
+jmethodID method_glonassSatelliteEphemerisGetAgeInDays;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_glonassSatelliteEphemerisGetHealthState;
+jmethodID method_glonassSatelliteEphemerisGetSlotNumber;
+jmethodID method_glonassSatelliteEphemerisGetFrameTimeSeconds;
+jmethodID method_glonassSatelliteEphemerisGetUpdateIntervalMinutes;
+jmethodID method_glonassSatelliteEphemerisGetIsGlonassM;
+jmethodID method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd;
+
+jmethodID method_glonassSatelliteOrbitModelGetX;
+jmethodID method_glonassSatelliteOrbitModelGetY;
+jmethodID method_glonassSatelliteOrbitModelGetZ;
+jmethodID method_glonassSatelliteOrbitModelGetXAccel;
+jmethodID method_glonassSatelliteOrbitModelGetYAccel;
+jmethodID method_glonassSatelliteOrbitModelGetZAccel;
+jmethodID method_glonassSatelliteOrbitModelGetXDot;
+jmethodID method_glonassSatelliteOrbitModelGetYDot;
+jmethodID method_glonassSatelliteOrbitModelGetZDot;
+jmethodID method_glonassSatelliteClockModelGetClockBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteClockModelGetTimeOfClockSeconds;
+
+jmethodID method_qzssAssistanceGetAlmanac;
+jmethodID method_qzssAssistanceGetIonosphericModel;
+jmethodID method_qzssAssistanceGetUtcModel;
+jmethodID method_qzssAssistanceGetLeapSecondsModel;
+jmethodID method_qzssAssistanceGetTimeModels;
+jmethodID method_qzssAssistanceGetSatelliteEphemeris;
+jmethodID method_qzssAssistanceGetSatelliteCorrections;
+jmethodID method_qzssAssistanceGetRealTimeIntegrityModels;
+jmethodID method_qzssSatelliteEphemerisGetSvid;
+jmethodID method_qzssSatelliteEphemerisGetGpsL2Params;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_qzssSatelliteClockModelGetAf0;
+jmethodID method_qzssSatelliteClockModelGetAf1;
+jmethodID method_qzssSatelliteClockModelGetAf2;
+jmethodID method_qzssSatelliteClockModelGetAodc;
+jmethodID method_qzssSatelliteClockModelGetTgd1;
+jmethodID method_qzssSatelliteClockModelGetTgd2;
+jmethodID method_qzssSatelliteClockModelGetTimeOfClockSeconds;
+} // namespace
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz) {
+ // Get the methods of GnssAssistance class.
+ jclass gnssAssistanceClass = env->FindClass("android/location/GnssAssistance");
+
+ method_gnssAssistanceGetGpsAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGpsAssistance",
+ "()Landroid/location/GpsAssistance;");
+ method_gnssAssistanceGetGlonassAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGlonassAssistance",
+ "()Landroid/location/GlonassAssistance;");
+ method_gnssAssistanceGetGalileoAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGalileoAssistance",
+ "()Landroid/location/GalileoAssistance;");
+ method_gnssAssistanceGetBeidouAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getBeidouAssistance",
+ "()Landroid/location/BeidouAssistance;");
+ method_gnssAssistanceGetQzssAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getQzssAssistance",
+ "()Landroid/location/QzssAssistance;");
+
+ // Get the methods of List class.
+ jclass listClass = env->FindClass("java/util/List");
+
+ method_listSize = env->GetMethodID(listClass, "size", "()I");
+ method_listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+
+ // Get the methods of GnssAlmanac class.
+ jclass gnssAlmanacClass = env->FindClass("android/location/GnssAlmanac");
+
+ method_gnssAlmanacGetIssueDateMillis =
+ env->GetMethodID(gnssAlmanacClass, "getIssueDateMillis", "()J");
+ method_gnssAlmanacGetIoda = env->GetMethodID(gnssAlmanacClass, "getIoda", "()I");
+ method_gnssAlmanacGetWeekNumber = env->GetMethodID(gnssAlmanacClass, "getWeekNumber", "()I");
+ method_gnssAlmanacGetToaSeconds = env->GetMethodID(gnssAlmanacClass, "getToaSeconds", "()I");
+ method_gnssAlmanacGetSatelliteAlmanacs =
+ env->GetMethodID(gnssAlmanacClass, "getGnssSatelliteAlmanacs", "()Ljava/util/List;");
+ method_gnssAlmanacIsCompleteAlmanacProvided =
+ env->GetMethodID(gnssAlmanacClass, "isCompleteAlmanacProvided", "()Z");
+
+ // Get the methods of SatelliteAlmanac class.
+ jclass satelliteAlmanacClass =
+ env->FindClass("android/location/GnssAlmanac$GnssSatelliteAlmanac");
+
+ method_satelliteAlmanacGetSvid = env->GetMethodID(satelliteAlmanacClass, "getSvid", "()I");
+ method_satelliteAlmanacGetSvHealth =
+ env->GetMethodID(satelliteAlmanacClass, "getSvHealth", "()I");
+ method_satelliteAlmanacGetAf0 = env->GetMethodID(satelliteAlmanacClass, "getAf0", "()D");
+ method_satelliteAlmanacGetAf1 = env->GetMethodID(satelliteAlmanacClass, "getAf1", "()D");
+ method_satelliteAlmanacGetEccentricity =
+ env->GetMethodID(satelliteAlmanacClass, "getEccentricity", "()D");
+ method_satelliteAlmanacGetInclination =
+ env->GetMethodID(satelliteAlmanacClass, "getInclination", "()D");
+ method_satelliteAlmanacGetM0 = env->GetMethodID(satelliteAlmanacClass, "getM0", "()D");
+ method_satelliteAlmanacGetOmega = env->GetMethodID(satelliteAlmanacClass, "getOmega", "()D");
+ method_satelliteAlmanacGetOmega0 = env->GetMethodID(satelliteAlmanacClass, "getOmega0", "()D");
+ method_satelliteAlmanacGetOmegaDot =
+ env->GetMethodID(satelliteAlmanacClass, "getOmegaDot", "()D");
+ method_satelliteAlmanacGetRootA = env->GetMethodID(satelliteAlmanacClass, "getRootA", "()D");
+
+ // Get the mothods of SatelliteEphemerisTime class.
+ jclass satelliteEphemerisTimeClass = env->FindClass("android/location/SatelliteEphemerisTime");
+
+ method_satelliteEphemerisTimeGetIode =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getIode", "()I");
+ method_satelliteEphemerisTimeGetToeSeconds =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getToeSeconds", "()I");
+ method_satelliteEphemerisTimeGetWeekNumber =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getWeekNumber", "()I");
+
+ // Get the mothods of KeplerianOrbitModel class.
+ jclass keplerianOrbitModelClass = env->FindClass("android/location/KeplerianOrbitModel");
+
+ method_keplerianOrbitModelGetDeltaN =
+ env->GetMethodID(keplerianOrbitModelClass, "getDeltaN", "()D");
+ method_keplerianOrbitModelGetEccentricity =
+ env->GetMethodID(keplerianOrbitModelClass, "getEccentricity", "()D");
+ method_keplerianOrbitModelGetI0 = env->GetMethodID(keplerianOrbitModelClass, "getI0", "()D");
+ method_keplerianOrbitModelGetIDot =
+ env->GetMethodID(keplerianOrbitModelClass, "getIDot", "()D");
+ method_keplerianOrbitModelGetM0 = env->GetMethodID(keplerianOrbitModelClass, "getM0", "()D");
+ method_keplerianOrbitModelGetOmega =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmega", "()D");
+ method_keplerianOrbitModelGetOmega0 =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmega0", "()D");
+ method_keplerianOrbitModelGetOmegaDot =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmegaDot", "()D");
+ method_keplerianOrbitModelGetRootA =
+ env->GetMethodID(keplerianOrbitModelClass, "getRootA", "()D");
+ method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation =
+ env->GetMethodID(keplerianOrbitModelClass, "getSecondOrderHarmonicPerturbation",
+ "()Landroid/location/"
+ "KeplerianOrbitModel$SecondOrderHarmonicPerturbation;");
+
+ // Get the methods of SecondOrderHarmonicPerturbation class.
+ jclass secondOrderHarmonicPerturbationClass =
+ env->FindClass("android/location/KeplerianOrbitModel$SecondOrderHarmonicPerturbation");
+
+ method_secondOrderHarmonicPerturbationGetCic =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCic", "()D");
+ method_secondOrderHarmonicPerturbationGetCis =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCis", "()D");
+ method_secondOrderHarmonicPerturbationGetCrc =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrc", "()D");
+ method_secondOrderHarmonicPerturbationGetCrs =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrs", "()D");
+ method_secondOrderHarmonicPerturbationGetCuc =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCuc", "()D");
+ method_secondOrderHarmonicPerturbationGetCus =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCus", "()D");
+
+ // Get the methods of KlobucharIonosphericModel class.
+ jclass klobucharIonosphericModelClass =
+ env->FindClass("android/location/KlobucharIonosphericModel");
+
+ method_klobucharIonosphericModelGetAlpha0 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha0", "()D");
+ method_klobucharIonosphericModelGetAlpha1 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha1", "()D");
+ method_klobucharIonosphericModelGetAlpha2 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha2", "()D");
+ method_klobucharIonosphericModelGetAlpha3 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha3", "()D");
+ method_klobucharIonosphericModelGetBeta0 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta0", "()D");
+ method_klobucharIonosphericModelGetBeta1 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta1", "()D");
+ method_klobucharIonosphericModelGetBeta2 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta2", "()D");
+ method_klobucharIonosphericModelGetBeta3 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta3", "()D");
+
+ // Get the methods of UtcModel class.
+ jclass utcModelClass = env->FindClass("android/location/UtcModel");
+
+ method_utcModelGetA0 = env->GetMethodID(utcModelClass, "getA0", "()D");
+ method_utcModelGetA1 = env->GetMethodID(utcModelClass, "getA1", "()D");
+ method_utcModelGetTimeOfWeek = env->GetMethodID(utcModelClass, "getTimeOfWeek", "()I");
+ method_utcModelGetWeekNumber = env->GetMethodID(utcModelClass, "getWeekNumber", "()I");
+
+ // Get the methods of LeapSecondsModel class.
+ jclass leapSecondsModelClass = env->FindClass("android/location/LeapSecondsModel");
+
+ method_leapSecondsModelGetDayNumberLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getDayNumberLeapSecondsFuture", "()I");
+ method_leapSecondsModelGetLeapSeconds =
+ env->GetMethodID(leapSecondsModelClass, "getLeapSeconds", "()I");
+ method_leapSecondsModelGetLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getLeapSecondsFuture", "()I");
+ method_leapSecondsModelGetWeekNumberLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getWeekNumberLeapSecondsFuture", "()I");
+
+ // Get the methods of TimeModel class.
+ jclass timeModelsClass = env->FindClass("android/location/TimeModel");
+
+ method_timeModelsGetTimeOfWeek = env->GetMethodID(timeModelsClass, "getTimeOfWeek", "()I");
+ method_timeModelsGetToGnss = env->GetMethodID(timeModelsClass, "getToGnss", "()I");
+ method_timeModelsGetWeekNumber = env->GetMethodID(timeModelsClass, "getWeekNumber", "()I");
+ method_timeModelsGetA0 = env->GetMethodID(timeModelsClass, "getA0", "()D");
+ method_timeModelsGetA1 = env->GetMethodID(timeModelsClass, "getA1", "()D");
+
+ // Get the methods of AuxiliaryInformation class.
+ jclass auxiliaryInformationClass = env->FindClass("android/location/AuxiliaryInformation");
+
+ method_auxiliaryInformationGetSvid =
+ env->GetMethodID(auxiliaryInformationClass, "getSvid", "()I");
+ method_auxiliaryInformationGetAvailableSignalTypes =
+ env->GetMethodID(auxiliaryInformationClass, "getAvailableSignalTypes",
+ "()Ljava/util/List;");
+ method_auxiliaryInformationGetFrequencyChannelNumber =
+ env->GetMethodID(auxiliaryInformationClass, "getFrequencyChannelNumber", "()I");
+ method_auxiliaryInformationGetSatType =
+ env->GetMethodID(auxiliaryInformationClass, "getSatType", "()I");
+
+ // Get the methods of RealTimeIntegrityModel
+ jclass realTimeIntegrityModelClass = env->FindClass("android/location/RealTimeIntegrityModel");
+
+ method_realTimeIntegrityModelGetBadSvid =
+ env->GetMethodID(realTimeIntegrityModelClass, "getBadSvid", "()I");
+ method_realTimeIntegrityModelGetBadSignalTypes =
+ env->GetMethodID(realTimeIntegrityModelClass, "getBadSignalTypes",
+ "()Ljava/util/List;");
+ method_realTimeIntegrityModelGetStartDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getStartDateSeconds", "()J");
+ method_realTimeIntegrityModelGetEndDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getEndDateSeconds", "()J");
+ method_realTimeIntegrityModelGetPublishDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getPublishDateSeconds", "()J");
+ method_realTimeIntegrityModelGetAdvisoryNumber =
+ env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryNumber",
+ "()Ljava/lang/String;");
+ method_realTimeIntegrityModelGetAdvisoryType =
+ env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryType",
+ "()Ljava/lang/String;");
+
+ // Get the methods of GnssSignalType class.
+ jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+
+ method_gnssSignalTypeGetConstellationType =
+ env->GetMethodID(gnssSignalTypeClass, "getConstellationType", "()I");
+ method_gnssSignalTypeGetCarrierFrequencyHz =
+ env->GetMethodID(gnssSignalTypeClass, "getCarrierFrequencyHz", "()D");
+ method_gnssSignalTypeGetCodeType =
+ env->GetMethodID(gnssSignalTypeClass, "getCodeType", "()Ljava/lang/String;");
+
+ // Get the methods of SatelliteCorrection class.
+ jclass satelliteCorrectionClass =
+ env->FindClass("android/location/GnssAssistance$GnssSatelliteCorrections");
+
+ method_satelliteCorrectionGetSvid =
+ env->GetMethodID(satelliteCorrectionClass, "getSvid", "()I");
+ method_satelliteCorrectionGetIonosphericCorrections =
+ env->GetMethodID(satelliteCorrectionClass, "getIonosphericCorrections",
+ "()Ljava/util/List;");
+
+ // Get the methods of IonosphericCorrection class.
+ jclass ionosphericCorrectionClass = env->FindClass("android/location/IonosphericCorrection");
+
+ method_ionosphericCorrectionGetCarrierFrequencyHz =
+ env->GetMethodID(ionosphericCorrectionClass, "getCarrierFrequencyHz", "()J");
+ method_ionosphericCorrectionGetIonosphericCorrection =
+ env->GetMethodID(ionosphericCorrectionClass, "getIonosphericCorrection",
+ "()Landroid/location/GnssCorrectionComponent;");
+
+ // Get the methods of GnssCorrectionComponent class.
+ jclass gnssCorrectionComponentClass =
+ env->FindClass("android/location/GnssCorrectionComponent");
+
+ method_gnssCorrectionComponentGetPseudorangeCorrection =
+ env->GetMethodID(gnssCorrectionComponentClass, "getPseudorangeCorrection",
+ "()Landroid/location/GnssCorrectionComponent$PseudorangeCorrection;");
+ method_gnssCorrectionComponentGetSourceKey =
+ env->GetMethodID(gnssCorrectionComponentClass, "getSourceKey", "()Ljava/lang/String;");
+ method_gnssCorrectionComponentGetValidityInterval =
+ env->GetMethodID(gnssCorrectionComponentClass, "getValidityInterval",
+ "()Landroid/location/GnssCorrectionComponent$GnssInterval;");
+
+ // Get the methods of PseudorangeCorrection class.
+ jclass pseudorangeCorrectionClass =
+ env->FindClass("android/location/GnssCorrectionComponent$PseudorangeCorrection");
+
+ method_pseudorangeCorrectionGetCorrectionMeters =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionMeters", "()D");
+ method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionRateMetersPerSecond", "()D");
+ method_pseudorangeCorrectionGetCorrectionUncertaintyMeters =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionUncertaintyMeters", "()D");
+
+ // Get the methods of GnssInterval class.
+ jclass gnssIntervalClass =
+ env->FindClass("android/location/GnssCorrectionComponent$GnssInterval");
+
+ method_gnssIntervalGetStartMillisSinceGpsEpoch =
+ env->GetMethodID(gnssIntervalClass, "getStartMillisSinceGpsEpoch", "()J");
+ method_gnssIntervalGetEndMillisSinceGpsEpoch =
+ env->GetMethodID(gnssIntervalClass, "getEndMillisSinceGpsEpoch", "()J");
+
+ // Get the methods of GpsAssistance class.
+ jclass gpsAssistanceClass = env->FindClass("android/location/GpsAssistance");
+
+ method_gpsAssistanceGetAlmanac =
+ env->GetMethodID(gpsAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+ method_gpsAssistanceGetIonosphericModel =
+ env->GetMethodID(gpsAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_gpsAssistanceGetUtcModel =
+ env->GetMethodID(gpsAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_gpsAssistanceGetLeapSecondsModel =
+ env->GetMethodID(gpsAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_gpsAssistanceGetTimeModels =
+ env->GetMethodID(gpsAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_gpsAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(gpsAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_gpsAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(gpsAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+ method_gpsAssistanceGetSatelliteCorrections =
+ env->GetMethodID(gpsAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+ // Get the methods of GpsSatelliteEphemeris class.
+ jclass gpsSatelliteEphemerisClass = env->FindClass("android/location/GpsSatelliteEphemeris");
+
+ method_gpsSatelliteEphemerisGetSvid =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSvid", "()I");
+ method_gpsSatelliteEphemerisGetGpsL2Params =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getGpsL2Params",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+ method_gpsSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteClockModel;");
+ method_gpsSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+ method_gpsSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+ method_gpsSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+
+ // Get the methods of GpsL2Params class.
+ jclass gpsL2ParamsClass = env->FindClass("android/location/GpsSatelliteEphemeris$GpsL2Params");
+ method_gpsL2ParamsGetL2Code = env->GetMethodID(gpsL2ParamsClass, "getL2Code", "()I");
+ method_gpsL2ParamsGetL2Flag = env->GetMethodID(gpsL2ParamsClass, "getL2Flag", "()I");
+
+ // Get the methods of GpsSatelliteClockModel class.
+ jclass gpsSatelliteClockModelClass =
+ env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteClockModel");
+ method_gpsSatelliteClockModelGetAf0 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf0", "()D");
+ method_gpsSatelliteClockModelGetAf1 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf1", "()D");
+ method_gpsSatelliteClockModelGetAf2 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf2", "()D");
+ method_gpsSatelliteClockModelGetTgd =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getTgd", "()D");
+ method_gpsSatelliteClockModelGetIodc =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getIodc", "()I");
+ method_gpsSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of GpsSatelliteHealth class.
+ jclass gpsSatelliteHealthClass =
+ env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteHealth");
+ method_gpsSatelliteHealthGetFitInt =
+ env->GetMethodID(gpsSatelliteHealthClass, "getFitInt", "()D");
+ method_gpsSatelliteHealthGetSvAccur =
+ env->GetMethodID(gpsSatelliteHealthClass, "getSvAccur", "()D");
+ method_gpsSatelliteHealthGetSvHealth =
+ env->GetMethodID(gpsSatelliteHealthClass, "getSvHealth", "()I");
+
+ // Get the methods of BeidouAssistance class.
+ jclass beidouAssistanceClass = env->FindClass("android/location/BeidouAssistance");
+ method_beidouAssistanceGetAlmanac = env->GetMethodID(beidouAssistanceClass, "getAlmanac",
+ "()Landroid/location/GnssAlmanac;");
+ method_beidouAssistanceGetIonosphericModel =
+ env->GetMethodID(beidouAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_beidouAssistanceGetUtcModel =
+ env->GetMethodID(beidouAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_beidouAssistanceGetLeapSecondsModel =
+ env->GetMethodID(beidouAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_beidouAssistanceGetTimeModels =
+ env->GetMethodID(beidouAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_beidouAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(beidouAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_beidouAssistanceGetSatelliteCorrections =
+ env->GetMethodID(beidouAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+ method_beidouAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(beidouAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+
+ // Get the methods of BeidouSatelliteEphemeris class.
+ jclass beidouSatelliteEphemerisClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris");
+ method_beidouSatelliteEphemerisGetSvid =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSvid", "()I");
+ method_beidouSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/"
+ "BeidouSatelliteEphemeris$BeidouSatelliteClockModel;");
+ method_beidouSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+ method_beidouSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth;");
+ method_beidouSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/"
+ "BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime;");
+
+ // Get the methods of BeidouSatelliteClockModel
+ jclass beidouSatelliteClockModelClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteClockModel");
+ method_beidouSatelliteClockModelGetAf0 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf0", "()D");
+ method_beidouSatelliteClockModelGetAf1 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf1", "()D");
+ method_beidouSatelliteClockModelGetAf2 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf2", "()D");
+ method_beidouSatelliteClockModelGetAodc =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAodc", "()I");
+ method_beidouSatelliteClockModelGetTgd1 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTgd1", "()D");
+ method_beidouSatelliteClockModelGetTgd2 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTgd2", "()D");
+ method_beidouSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of BeidouSatelliteHealth
+ jclass beidouSatelliteHealthClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth");
+ method_beidouSatelliteHealthGetSatH1 =
+ env->GetMethodID(beidouSatelliteHealthClass, "getSatH1", "()I");
+ method_beidouSatelliteHealthGetSvAccur =
+ env->GetMethodID(beidouSatelliteHealthClass, "getSvAccur", "()D");
+
+ // Get the methods of BeidouSatelliteEphemerisTime
+ jclass beidouSatelliteEphemerisTimeClass = env->FindClass(
+ "android/location/BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime");
+ method_beidouSatelliteEphemerisTimeGetIode =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getIode", "()I");
+ method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getBeidouWeekNumber", "()I");
+ method_beidouSatelliteEphemerisTimeGetToeSeconds =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getToeSeconds", "()I");
+
+ // Get the methods of GalileoAssistance class.
+ jclass galileoAssistanceClass = env->FindClass("android/location/GalileoAssistance");
+ method_galileoAssistanceGetAlmanac = env->GetMethodID(galileoAssistanceClass, "getAlmanac",
+ "()Landroid/location/GnssAlmanac;");
+ method_galileoAssistanceGetIonosphericModel =
+ env->GetMethodID(galileoAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_galileoAssistanceGetUtcModel = env->GetMethodID(galileoAssistanceClass, "getUtcModel",
+ "()Landroid/location/UtcModel;");
+ method_galileoAssistanceGetLeapSecondsModel =
+ env->GetMethodID(galileoAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_galileoAssistanceGetTimeModels =
+ env->GetMethodID(galileoAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_galileoAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(galileoAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_galileoAssistanceGetSatelliteCorrections =
+ env->GetMethodID(galileoAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+ method_galileoAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(galileoAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+
+ // Get the methods of GalileoSatelliteEphemeris class
+ jclass galileoSatelliteEphemerisClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris");
+ method_galileoSatelliteEphemerisGetSatelliteClockModels =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteClockModels",
+ "()Ljava/util/List;");
+ method_galileoSatelliteEphemerisGetSvid =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSvid", "()I");
+ method_galileoSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+ method_galileoSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GalileoSatelliteEphemeris$GalileoSvHealth;");
+ method_galileoSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+
+ // Get the methods of GalileoSatelliteClockModel class.
+ jclass galileoSatelliteClockModelClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSatelliteClockModel");
+ method_galileoSatelliteClockModelGetAf0 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf0", "()D");
+ method_galileoSatelliteClockModelGetAf1 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf1", "()D");
+ method_galileoSatelliteClockModelGetAf2 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf2", "()D");
+ method_galileoSatelliteClockModelGetBgdSeconds =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getBgdSeconds", "()D");
+ method_galileoSatelliteClockModelGetSatelliteClockType =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getSatelliteClockType", "()I");
+ method_galileoSatelliteClockModelGetSisaMeters =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getSisaMeters", "()D");
+ method_galileoSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of GalileoSvHealth class.
+ jclass galileoSvHealthClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSvHealth");
+ method_galileoSvHealthGetDataValidityStatusE1b =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE1b", "()I");
+ method_galileoSvHealthGetDataValidityStatusE5a =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5a", "()I");
+ method_galileoSvHealthGetDataValidityStatusE5b =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5b", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE1b =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE1b", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE5a =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5a", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE5b =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5b", "()I");
+
+ // Get the methods of GalileoIonosphericModel class.
+ jclass galileoIonosphericModelClass =
+ env->FindClass("android/location/GalileoIonosphericModel");
+ method_galileoIonosphericModelGetAi0 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi0", "()D");
+ method_galileoIonosphericModelGetAi1 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi1", "()D");
+ method_galileoIonosphericModelGetAi2 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi2", "()D");
+
+ // Get the methods of GlonassAssistance class.
+ jclass glonassAssistanceClass = env->FindClass("android/location/GlonassAssistance");
+ method_glonassAssistanceGetAlmanac = env->GetMethodID(glonassAssistanceClass, "getAlmanac",
+ "()Landroid/location/GlonassAlmanac;");
+ method_glonassAssistanceGetUtcModel = env->GetMethodID(glonassAssistanceClass, "getUtcModel",
+ "()Landroid/location/UtcModel;");
+ method_glonassAssistanceGetTimeModels =
+ env->GetMethodID(glonassAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_glonassAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(glonassAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_glonassAssistanceGetSatelliteCorrections =
+ env->GetMethodID(glonassAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+
+ // Get the methods of GlonassAlmanac class.
+ jclass glonassAlmanacClass = env->FindClass("android/location/GlonassAlmanac");
+ method_glonassAlmanacGetIssueDateMillis =
+ env->GetMethodID(glonassAlmanacClass, "getIssueDateMillis", "()J");
+ method_glonassAlmanacGetSatelliteAlmanacs =
+ env->GetMethodID(glonassAlmanacClass, "getSatelliteAlmanacs", "()Ljava/util/List;");
+
+ // Get the methods of GlonassSatelliteAlmanac class
+ jclass glonassSatelliteAlmanacClass =
+ env->FindClass("android/location/GlonassAlmanac$GlonassSatelliteAlmanac");
+ method_glonassSatelliteAlmanacGetDeltaI =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaI", "()D");
+ method_glonassSatelliteAlmanacGetDeltaT =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaT", "()D");
+ method_glonassSatelliteAlmanacGetDeltaTDot =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaTDot", "()D");
+ method_glonassSatelliteAlmanacGetEccentricity =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getEccentricity", "()D");
+ method_glonassSatelliteAlmanacGetFrequencyChannelNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getFrequencyChannelNumber", "()I");
+ method_glonassSatelliteAlmanacGetLambda =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getLambda", "()D");
+ method_glonassSatelliteAlmanacGetOmega =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getOmega", "()D");
+ method_glonassSatelliteAlmanacGetSlotNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getSlotNumber", "()I");
+ method_glonassSatelliteAlmanacGetHealthState =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getHealthState", "()I");
+ method_glonassSatelliteAlmanacGetTLambda =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getTLambda", "()D");
+ method_glonassSatelliteAlmanacGetTau =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getTau", "()D");
+ method_glonassSatelliteAlmanacGetCalendarDayNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getCalendarDayNumber", "()I");
+ method_glonassSatelliteAlmanacGetIsGlonassM =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "isGlonassM", "()Z");
+
+ // Get the methods of GlonassSatelliteEphemeris
+ jclass glonassSatelliteEphemerisClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris");
+ method_glonassSatelliteEphemerisGetAgeInDays =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getAgeInDays", "()I");
+ method_glonassSatelliteEphemerisGetFrameTimeSeconds =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getFrameTimeSeconds", "()D");
+ method_glonassSatelliteEphemerisGetHealthState =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getHealthState", "()I");
+ method_glonassSatelliteEphemerisGetSlotNumber =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSlotNumber", "()I");
+ method_glonassSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/"
+ "GlonassSatelliteEphemeris$GlonassSatelliteClockModel;");
+ method_glonassSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/"
+ "GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel;");
+ method_glonassSatelliteEphemerisGetUpdateIntervalMinutes =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getUpdateIntervalMinutes", "()I");
+ method_glonassSatelliteEphemerisGetIsGlonassM =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "isGlonassM", "()Z");
+ method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "isUpdateIntervalOdd", "()Z");
+
+ // Get the methods of GlonassSatelliteOrbitModel
+ jclass glonassSatelliteOrbitModelClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel");
+ method_glonassSatelliteOrbitModelGetX =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getX", "()D");
+ method_glonassSatelliteOrbitModelGetXAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getXAccel", "()D");
+ method_glonassSatelliteOrbitModelGetXDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getXDot", "()D");
+ method_glonassSatelliteOrbitModelGetY =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getY", "()D");
+ method_glonassSatelliteOrbitModelGetYAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getYAccel", "()D");
+ method_glonassSatelliteOrbitModelGetYDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getYDot", "()D");
+ method_glonassSatelliteOrbitModelGetZ =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZ", "()D");
+ method_glonassSatelliteOrbitModelGetZAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZAccel", "()D");
+ method_glonassSatelliteOrbitModelGetZDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZDot", "()D");
+
+ // Get the methods of GlonassSatelliteClockModel
+ jclass glonassSatelliteClockModelClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteClockModel");
+ method_glonassSatelliteClockModelGetClockBias =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getClockBias", "()D");
+ method_glonassSatelliteClockModelGetFrequencyBias =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyBias", "()D");
+ method_glonassSatelliteClockModelGetFrequencyChannelNumber =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyChannelNumber", "()I");
+ method_glonassSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of QzssAssistance class.
+ jclass qzssAssistanceClass = env->FindClass("android/location/QzssAssistance");
+ method_qzssAssistanceGetAlmanac =
+ env->GetMethodID(qzssAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+ method_qzssAssistanceGetIonosphericModel =
+ env->GetMethodID(qzssAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_qzssAssistanceGetUtcModel =
+ env->GetMethodID(qzssAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_qzssAssistanceGetLeapSecondsModel =
+ env->GetMethodID(qzssAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_qzssAssistanceGetTimeModels =
+ env->GetMethodID(qzssAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_qzssAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(qzssAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_qzssAssistanceGetSatelliteCorrections =
+ env->GetMethodID(qzssAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+ // Get the methods of QzssSatelliteEphemeris class.
+ jclass qzssSatelliteEphemerisClass = env->FindClass("android/location/QzssSatelliteEphemeris");
+ method_qzssSatelliteEphemerisGetSvid =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSvid", "()I");
+ method_qzssSatelliteEphemerisGetGpsL2Params =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getGpsL2Params",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+ method_qzssSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+ method_qzssSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+ method_qzssSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+}
+
+GnssAssistanceInterface::GnssAssistanceInterface(
+ const sp<IGnssAssistanceInterface>& iGnssAssistance)
+ : mGnssAssistanceInterface(iGnssAssistance) {
+ assert(mGnssAssistanceInterface != nullptr);
+}
+
+jboolean GnssAssistanceInterface::injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj) {
+ GnssAssistance gnssAssistance;
+ GnssAssistanceUtil::setGnssAssistance(env, gnssAssistanceObj, gnssAssistance);
+ auto status = mGnssAssistanceInterface->injectGnssAssistance(gnssAssistance);
+ return checkAidlStatus(status, "IGnssAssistanceInterface injectGnssAssistance() failed.");
+}
+
+jboolean GnssAssistanceInterface::setCallback(const sp<IGnssAssistanceCallback>& callback) {
+ auto status = mGnssAssistanceInterface->setCallback(callback);
+ return checkAidlStatus(status, "IGnssAssistanceInterface setCallback() failed.");
+}
+
+void GnssAssistanceUtil::setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+ GnssAssistance& gnssAssistance) {
+ jobject gpsAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGpsAssistance);
+ jobject glonassAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGlonassAssistance);
+ jobject qzssAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetQzssAssistance);
+ jobject galileoAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGalileoAssistance);
+ jobject beidouAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetBeidouAssistance);
+ GnssAssistanceUtil::setGpsAssistance(env, gpsAssistanceObj, gnssAssistance.gpsAssistance);
+ GnssAssistanceUtil::setGlonassAssistance(env, glonassAssistanceObj,
+ gnssAssistance.glonassAssistance);
+ GnssAssistanceUtil::setQzssAssistance(env, qzssAssistanceObj, gnssAssistance.qzssAssistance);
+ GnssAssistanceUtil::setGalileoAssistance(env, galileoAssistanceObj,
+ gnssAssistance.galileoAssistance);
+ GnssAssistanceUtil::setBeidouAssistance(env, beidouAssistanceObj,
+ gnssAssistance.beidouAssistance);
+ env->DeleteLocalRef(gpsAssistanceObj);
+ env->DeleteLocalRef(glonassAssistanceObj);
+ env->DeleteLocalRef(qzssAssistanceObj);
+ env->DeleteLocalRef(galileoAssistanceObj);
+ env->DeleteLocalRef(beidouAssistanceObj);
+}
+
+void GnssAssistanceUtil::setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+ QzssAssistance& qzssAssistance) {
+ jobject qzssAlmanacObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetAlmanac);
+ jobject qzssIonosphericModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetIonosphericModel);
+ jobject qzssUtcModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetUtcModel);
+ jobject qzssLeapSecondsModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetLeapSecondsModel);
+ jobject qzssTimeModelsObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetTimeModels);
+ jobject qzssSatelliteEphemerisObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteEphemeris);
+ jobject qzssSatelliteCorrectionsObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, qzssAlmanacObj, qzssAssistance.almanac);
+ setKlobucharIonosphericModel(env, qzssIonosphericModelObj, qzssAssistance.ionosphericModel);
+ setUtcModel(env, qzssUtcModelObj, qzssAssistance.utcModel);
+ setLeapSecondsModel(env, qzssLeapSecondsModelObj, qzssAssistance.leapSecondsModel);
+ setTimeModels(env, qzssTimeModelsObj, qzssAssistance.timeModels);
+ setGpsOrQzssSatelliteEphemeris<QzssSatelliteEphemeris>(env, qzssSatelliteEphemerisObj,
+ qzssAssistance.satelliteEphemeris);
+ setSatelliteCorrections(env, qzssSatelliteCorrectionsObj, qzssAssistance.satelliteCorrections);
+ env->DeleteLocalRef(qzssAlmanacObj);
+ env->DeleteLocalRef(qzssIonosphericModelObj);
+ env->DeleteLocalRef(qzssUtcModelObj);
+ env->DeleteLocalRef(qzssLeapSecondsModelObj);
+ env->DeleteLocalRef(qzssTimeModelsObj);
+ env->DeleteLocalRef(qzssSatelliteEphemerisObj);
+ env->DeleteLocalRef(qzssSatelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+ GlonassAssistance& galileoAssistance) {
+ jobject glonassAlmanacObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetAlmanac);
+ jobject utcModelObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetUtcModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(glonassAssistanceObj,
+ method_glonassAssistanceGetSatelliteEphemeris);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(glonassAssistanceObj,
+ method_glonassAssistanceGetSatelliteCorrections);
+ setGlonassAlmanac(env, glonassAlmanacObj, galileoAssistance.almanac);
+ setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+ setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+ setGlonassSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+ env->DeleteLocalRef(glonassAlmanacObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+ GlonassAlmanac& glonassAlmanac) {
+ if (glonassAlmanacObj == nullptr) {
+ glonassAlmanac.issueDateMs = -1;
+ return;
+ }
+ jlong issueDateMillis =
+ env->CallLongMethod(glonassAlmanacObj, method_glonassAlmanacGetIssueDateMillis);
+ glonassAlmanac.issueDateMs = issueDateMillis;
+ jobject satelliteAlmanacsObj =
+ env->CallObjectMethod(glonassAlmanacObj, method_glonassAlmanacGetSatelliteAlmanacs);
+ if (satelliteAlmanacsObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteAlmanacsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject glonassSatelliteAlmanacObj =
+ env->CallObjectMethod(satelliteAlmanacsObj, method_listGet, i);
+ if (glonassSatelliteAlmanacObj == nullptr) continue;
+ GlonassSatelliteAlmanac glonassSatelliteAlmanac;
+ jdouble deltaI = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaI);
+ glonassSatelliteAlmanac.deltaI = deltaI;
+ jdouble deltaT = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaT);
+ glonassSatelliteAlmanac.deltaT = deltaT;
+ jdouble deltaTDot = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaTDot);
+ glonassSatelliteAlmanac.deltaTDot = deltaTDot;
+ jdouble eccentricity = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetEccentricity);
+ glonassSatelliteAlmanac.eccentricity = eccentricity;
+ jint frequencyChannelNumber =
+ env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetFrequencyChannelNumber);
+ glonassSatelliteAlmanac.frequencyChannelNumber =
+ static_cast<int32_t>(frequencyChannelNumber);
+ jdouble lambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetLambda);
+ glonassSatelliteAlmanac.lambda = lambda;
+ jdouble omega = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetOmega);
+ glonassSatelliteAlmanac.omega = omega;
+ jint slotNumber = env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetSlotNumber);
+ glonassSatelliteAlmanac.slotNumber = static_cast<int32_t>(slotNumber);
+ jint healthState = env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetHealthState);
+ glonassSatelliteAlmanac.svHealth = static_cast<int32_t>(healthState);
+ jdouble tLambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetTLambda);
+ glonassSatelliteAlmanac.tLambda = tLambda;
+ jdouble tau = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetTau);
+ glonassSatelliteAlmanac.tau = tau;
+ jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetIsGlonassM);
+ glonassSatelliteAlmanac.isGlonassM = isGlonassM;
+ jint calendarDayNumber =
+ env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetCalendarDayNumber);
+ glonassSatelliteAlmanac.calendarDayNumber = static_cast<int32_t>(calendarDayNumber);
+ glonassAlmanac.satelliteAlmanacs.push_back(glonassSatelliteAlmanac);
+ env->DeleteLocalRef(glonassSatelliteAlmanacObj);
+ }
+ env->DeleteLocalRef(satelliteAlmanacsObj);
+}
+
+void GnssAssistanceUtil::setGlonassSatelliteEphemeris(
+ JNIEnv* env, jobject glonassSatelliteEphemerisListObj,
+ std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList) {
+ if (glonassSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(glonassSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject glonassSatelliteEphemerisObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisListObj, method_listGet, i);
+ if (glonassSatelliteEphemerisObj == nullptr) continue;
+ GlonassSatelliteEphemeris glonassSatelliteEphemeris;
+ jdouble ageInDays = env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetAgeInDays);
+ glonassSatelliteEphemeris.ageInDays = ageInDays;
+
+ // Set the GlonassSatelliteClockModel.
+ jobject glonassSatelliteClockModelObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSatelliteClockModel);
+ GlonassSatelliteClockModel glonassSatelliteClockModel;
+ jdouble clockBias = env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetClockBias);
+ glonassSatelliteClockModel.clockBias = clockBias;
+ jdouble frequencyBias =
+ env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetFrequencyBias);
+ glonassSatelliteClockModel.frequencyBias = frequencyBias;
+ jint frequencyChannelNumber =
+ env->CallIntMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetFrequencyChannelNumber);
+ glonassSatelliteClockModel.frequencyChannelNumber =
+ static_cast<int32_t>(frequencyChannelNumber);
+ jdouble timeOfClockSeconds =
+ env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetTimeOfClockSeconds);
+ glonassSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ glonassSatelliteEphemeris.satelliteClockModel = glonassSatelliteClockModel;
+ env->DeleteLocalRef(glonassSatelliteClockModelObj);
+
+ // Set the GlonassSatelliteOrbitModel.
+ jobject glonassSatelliteOrbitModelObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSatelliteOrbitModel);
+ GlonassSatelliteOrbitModel glonassSatelliteOrbitModel;
+ jdouble x = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetX);
+ glonassSatelliteOrbitModel.x = x;
+ jdouble y = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetY);
+ glonassSatelliteOrbitModel.y = y;
+ jdouble z = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZ);
+ glonassSatelliteOrbitModel.z = z;
+ jdouble xAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetXAccel);
+ glonassSatelliteOrbitModel.xAccel = xAccel;
+ jdouble yAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetYAccel);
+ glonassSatelliteOrbitModel.yAccel = yAccel;
+ jdouble zAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZAccel);
+ glonassSatelliteOrbitModel.zAccel = zAccel;
+ jdouble xDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetXDot);
+ glonassSatelliteOrbitModel.xDot = xDot;
+ jdouble yDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetYDot);
+ glonassSatelliteOrbitModel.yDot = yDot;
+ jdouble zDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZDot);
+ glonassSatelliteOrbitModel.zDot = zDot;
+ glonassSatelliteEphemeris.satelliteOrbitModel = glonassSatelliteOrbitModel;
+ env->DeleteLocalRef(glonassSatelliteOrbitModelObj);
+
+ jint healthState = env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetHealthState);
+ glonassSatelliteEphemeris.svHealth = static_cast<int32_t>(healthState);
+ jint slotNumber = env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSlotNumber);
+ glonassSatelliteEphemeris.slotNumber = static_cast<int32_t>(slotNumber);
+ jdouble frameTimeSeconds =
+ env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetFrameTimeSeconds);
+ glonassSatelliteEphemeris.frameTimeSeconds = frameTimeSeconds;
+ jint updateIntervalMinutes =
+ env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetUpdateIntervalMinutes);
+ glonassSatelliteEphemeris.updateIntervalMinutes =
+ static_cast<int32_t>(updateIntervalMinutes);
+ jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetIsGlonassM);
+ glonassSatelliteEphemeris.isGlonassM = isGlonassM;
+ jboolean isUpdateIntervalOdd =
+ env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd);
+ glonassSatelliteEphemeris.isOddUpdateInterval = isUpdateIntervalOdd;
+ glonassSatelliteEphemerisList.push_back(glonassSatelliteEphemeris);
+ env->DeleteLocalRef(glonassSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+ GalileoAssistance& galileoAssistance) {
+ jobject galileoAlmanacObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetIonosphericModel);
+ jobject utcModelObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, galileoAlmanacObj, galileoAssistance.almanac);
+ setGaliloKlobucharIonosphericModel(env, ionosphericModelObj,
+ galileoAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, galileoAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+ setGalileoSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ galileoAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+ env->DeleteLocalRef(galileoAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGaliloKlobucharIonosphericModel(
+ JNIEnv* env, jobject galileoIonosphericModelObj,
+ GalileoIonosphericModel& ionosphericModel) {
+ if (galileoIonosphericModelObj == nullptr) return;
+ jdouble ai0 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi0);
+ ionosphericModel.ai0 = ai0;
+ jdouble ai1 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi1);
+ ionosphericModel.ai1 = ai1;
+ jdouble ai2 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi2);
+ ionosphericModel.ai2 = ai2;
+}
+
+void GnssAssistanceUtil::setGalileoSatelliteEphemeris(
+ JNIEnv* env, jobject galileoSatelliteEphemerisListObj,
+ std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList) {
+ if (galileoSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(galileoSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject galileoSatelliteEphemerisObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisListObj, method_listGet, i);
+ GalileoSatelliteEphemeris galileoSatelliteEphemeris;
+ GalileoSvHealth galileoSvHealth;
+ // Set the svid of the satellite.
+ jint svid = env->CallLongMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSvid);
+ galileoSatelliteEphemeris.svid = svid;
+
+ // Set the satellite clock models.
+ jobject galileoSatelliteClockModelListObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteClockModels);
+ auto size = env->CallIntMethod(galileoSatelliteClockModelListObj, method_listSize);
+ for (uint16_t j = 0; j < size; ++j) {
+ jobject galileoSatelliteClockModelObj =
+ env->CallObjectMethod(galileoSatelliteClockModelListObj, method_listGet, j);
+ if (galileoSatelliteClockModelObj == nullptr) continue;
+ GalileoSatelliteClockModel galileoSatelliteClockModel;
+ jdouble af0 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf0);
+ galileoSatelliteClockModel.af0 = af0;
+ jdouble af1 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf1);
+ galileoSatelliteClockModel.af1 = af1;
+ jdouble af2 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf2);
+ galileoSatelliteClockModel.af2 = af2;
+ jdouble bgdSeconds =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetBgdSeconds);
+ galileoSatelliteClockModel.bgdSeconds = bgdSeconds;
+ jint satelliteClockType =
+ env->CallIntMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetSatelliteClockType);
+ galileoSatelliteClockModel.satelliteClockType =
+ static_cast<GalileoSatelliteClockModel::SatelliteClockType>(satelliteClockType);
+ jdouble sisaMeters =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetSisaMeters);
+ galileoSatelliteClockModel.sisaMeters = sisaMeters;
+ jdouble timeOfClockSeconds =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetTimeOfClockSeconds);
+ galileoSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ galileoSatelliteEphemeris.satelliteClockModel.push_back(galileoSatelliteClockModel);
+ env->DeleteLocalRef(galileoSatelliteClockModelObj);
+ }
+ env->DeleteLocalRef(galileoSatelliteClockModelListObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ galileoSatelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satellite health of the satellite clock model.
+ jobject galileoSvHealthObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteHealth);
+ jint dataValidityStatusE1b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE1b);
+ galileoSvHealth.dataValidityStatusE1b =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE1b);
+ jint dataValidityStatusE5a =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE5a);
+ galileoSvHealth.dataValidityStatusE5a =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5a);
+ jint dataValidityStatusE5b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE5b);
+ galileoSvHealth.dataValidityStatusE5b =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5b);
+ jint signalHealthStatusE1b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE1b);
+ galileoSvHealth.signalHealthStatusE1b =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE1b);
+ jint signalHealthStatusE5a =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE5a);
+ galileoSvHealth.signalHealthStatusE5a =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5a);
+ jint signalHealthStatusE5b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE5b);
+ galileoSvHealth.signalHealthStatusE5b =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5b);
+ galileoSatelliteEphemeris.svHealth = galileoSvHealth;
+ env->DeleteLocalRef(galileoSvHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteEphemerisTime);
+ GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+ galileoSatelliteEphemeris
+ .satelliteEphemerisTime);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ galileoSatelliteEphemerisList.push_back(galileoSatelliteEphemeris);
+ env->DeleteLocalRef(galileoSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+ BeidouAssistance& beidouAssistance) {
+ jobject beidouAlmanacObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetIonosphericModel);
+ jobject utcModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, beidouAlmanacObj, beidouAssistance.almanac);
+ setKlobucharIonosphericModel(env, ionosphericModelObj, beidouAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, beidouAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, beidouAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, beidouAssistance.timeModels);
+ setBeidouSatelliteEphemeris(env, satelliteEphemerisObj, beidouAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ beidouAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, beidouAssistance.satelliteCorrections);
+ env->DeleteLocalRef(beidouAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setBeidouSatelliteEphemeris(
+ JNIEnv* env, jobject beidouSatelliteEphemerisListObj,
+ std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList) {
+ if (beidouSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(beidouSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject beidouSatelliteEphemerisObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisListObj, method_listGet, i);
+ if (beidouSatelliteEphemerisObj == nullptr) continue;
+ BeidouSatelliteEphemeris beidouSatelliteEphemeris;
+
+ // Set the svid of the satellite.
+ jint svid = env->CallIntMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSvid);
+ beidouSatelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+ // Set the satelliteClockModel of the satellite.
+ jobject satelliteClockModelObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteClockModel);
+ jdouble af0 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf0);
+ jdouble af1 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf1);
+ jdouble af2 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf2);
+ jdouble tgd1 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTgd1);
+ jdouble tgd2 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTgd2);
+ jdouble aodc = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAodc);
+ jlong timeOfClockSeconds =
+ env->CallLongMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTimeOfClockSeconds);
+ beidouSatelliteEphemeris.satelliteClockModel.af0 = af0;
+ beidouSatelliteEphemeris.satelliteClockModel.af1 = af1;
+ beidouSatelliteEphemeris.satelliteClockModel.af2 = af2;
+ beidouSatelliteEphemeris.satelliteClockModel.tgd1 = tgd1;
+ beidouSatelliteEphemeris.satelliteClockModel.tgd2 = tgd2;
+ beidouSatelliteEphemeris.satelliteClockModel.aodc = aodc;
+ beidouSatelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ env->DeleteLocalRef(satelliteClockModelObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ beidouSatelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satelliteHealth of the satellite.
+ jobject satelliteHealthObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteHealth);
+ jint satH1 = env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSatH1);
+ jint svAccur =
+ env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSvAccur);
+ beidouSatelliteEphemeris.satelliteHealth.satH1 = static_cast<int32_t>(satH1);
+ beidouSatelliteEphemeris.satelliteHealth.svAccur = static_cast<int32_t>(svAccur);
+ env->DeleteLocalRef(satelliteHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteEphemerisTime);
+ jint iode = env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetIode);
+ jint beidouWeekNumber =
+ env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber);
+ jint toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetToeSeconds);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.aode = static_cast<int32_t>(iode);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.weekNumber =
+ static_cast<int32_t>(beidouWeekNumber);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.toeSeconds =
+ static_cast<int32_t>(toeSeconds);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ beidouSatelliteEphemerisList.push_back(beidouSatelliteEphemeris);
+ env->DeleteLocalRef(beidouSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+ GpsAssistance& gpsAssistance) {
+ jobject gnssAlmanacObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetIonosphericModel);
+ jobject utcModelObj = env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteCorrections);
+
+ setGnssAlmanac(env, gnssAlmanacObj, gpsAssistance.almanac);
+ setKlobucharIonosphericModel(env, ionosphericModelObj, gpsAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, gpsAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, gpsAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, gpsAssistance.timeModels);
+ setGpsOrQzssSatelliteEphemeris<GpsSatelliteEphemeris>(env, satelliteEphemerisObj,
+ gpsAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ gpsAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, gpsAssistance.satelliteCorrections);
+ env->DeleteLocalRef(gnssAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+/** Set the GPS/QZSS satellite ephemeris list. */
+template <class T>
+void GnssAssistanceUtil::setGpsOrQzssSatelliteEphemeris(JNIEnv* env,
+ jobject satelliteEphemerisListObj,
+ std::vector<T>& satelliteEphemerisList) {
+ if (satelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(satelliteEphemerisListObj, method_listGet, i);
+ if (satelliteEphemerisObj == nullptr) continue;
+ T satelliteEphemeris;
+ // Set the svid of the satellite.
+ jint svid = env->CallIntMethod(satelliteEphemerisObj, method_gpsSatelliteEphemerisGetSvid);
+ satelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+ // Set the gpsL2Params of the satellite.
+ jobject gpsL2ParamsObj = env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetGpsL2Params);
+ jint l2Code = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Code);
+ jint l2Flag = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Flag);
+ satelliteEphemeris.gpsL2Params.l2Code = static_cast<int32_t>(l2Code);
+ satelliteEphemeris.gpsL2Params.l2Flag = static_cast<int32_t>(l2Flag);
+ env->DeleteLocalRef(gpsL2ParamsObj);
+
+ // Set the satelliteClockModel of the satellite.
+ jobject satelliteClockModelObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteClockModel);
+ jdouble af0 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf0);
+ jdouble af1 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf1);
+ jdouble af2 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf2);
+ jdouble tgd =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetTgd);
+ jint iodc =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetIodc);
+ jlong timeOfClockSeconds =
+ env->CallLongMethod(satelliteClockModelObj,
+ method_gpsSatelliteClockModelGetTimeOfClockSeconds);
+ satelliteEphemeris.satelliteClockModel.af0 = af0;
+ satelliteEphemeris.satelliteClockModel.af1 = af1;
+ satelliteEphemeris.satelliteClockModel.af2 = af2;
+ satelliteEphemeris.satelliteClockModel.tgd = tgd;
+ satelliteEphemeris.satelliteClockModel.iodc = static_cast<int32_t>(iodc);
+ satelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ env->DeleteLocalRef(satelliteClockModelObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ satelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satelliteHealth of the satellite.
+ jobject satelliteHealthObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteHealth);
+ jint svHealth =
+ env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvHealth);
+ jdouble svAccur =
+ env->CallDoubleMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvAccur);
+ jdouble fitInt = env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetFitInt);
+ satelliteEphemeris.satelliteHealth.svHealth = static_cast<int32_t>(svHealth);
+ satelliteEphemeris.satelliteHealth.svAccur = svAccur;
+ satelliteEphemeris.satelliteHealth.fitInt = fitInt;
+ env->DeleteLocalRef(satelliteHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteEphemerisTime);
+ GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+ satelliteEphemeris.satelliteEphemerisTime);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ satelliteEphemerisList.push_back(satelliteEphemeris);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setSatelliteCorrections(
+ JNIEnv* env, jobject satelliteCorrectionsObj,
+ std::vector<GnssSatelliteCorrections>& gnssSatelliteCorrectionsList) {
+ if (satelliteCorrectionsObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteCorrectionsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ GnssSatelliteCorrections gnssSatelliteCorrections;
+ jobject satelliteCorrectionObj =
+ env->CallObjectMethod(satelliteCorrectionsObj, method_listGet, i);
+ if (satelliteCorrectionObj == nullptr) continue;
+ jint svid = env->CallIntMethod(satelliteCorrectionObj, method_satelliteCorrectionGetSvid);
+ gnssSatelliteCorrections.svid = svid;
+ jobject ionosphericCorrectionsObj =
+ env->CallObjectMethod(satelliteCorrectionObj,
+ method_satelliteCorrectionGetIonosphericCorrections);
+ env->DeleteLocalRef(satelliteCorrectionObj);
+ auto size = env->CallIntMethod(ionosphericCorrectionsObj, method_listSize);
+ for (uint16_t j = 0; j < size; ++j) {
+ jobject ionosphericCorrectionObj =
+ env->CallObjectMethod(ionosphericCorrectionsObj, method_listGet, j);
+ if (ionosphericCorrectionObj == nullptr) continue;
+ IonosphericCorrection ionosphericCorrection;
+ jlong carrierFrequencyHz =
+ env->CallLongMethod(ionosphericCorrectionObj,
+ method_ionosphericCorrectionGetCarrierFrequencyHz);
+ ionosphericCorrection.carrierFrequencyHz = carrierFrequencyHz;
+
+ jobject gnssCorrectionComponentObj =
+ env->CallObjectMethod(ionosphericCorrectionObj,
+ method_ionosphericCorrectionGetIonosphericCorrection);
+ env->DeleteLocalRef(ionosphericCorrectionObj);
+
+ jstring sourceKey = static_cast<jstring>(
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetSourceKey));
+ ScopedJniString jniSourceKey{env, sourceKey};
+ ionosphericCorrection.ionosphericCorrectionComponent.sourceKey =
+ android::String16(jniSourceKey.c_str());
+
+ jobject pseudorangeCorrectionObj =
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetPseudorangeCorrection);
+ jdouble correctionMeters =
+ env->CallDoubleMethod(pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionMeters);
+ jdouble correctionUncertaintyMeters = env->CallDoubleMethod(
+ pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionUncertaintyMeters);
+ jdouble correctionRateMetersPerSecond = env->CallDoubleMethod(
+ pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond);
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionMeters = correctionMeters;
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionUncertaintyMeters = correctionUncertaintyMeters;
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionRateMetersPerSecond = correctionRateMetersPerSecond;
+ env->DeleteLocalRef(pseudorangeCorrectionObj);
+
+ jobject gnssIntervalObj =
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetValidityInterval);
+ jdouble startMillisSinceGpsEpoch =
+ env->CallDoubleMethod(gnssIntervalObj,
+ method_gnssIntervalGetStartMillisSinceGpsEpoch);
+ jdouble endMillisSinceGpsEpoch =
+ env->CallDoubleMethod(gnssIntervalObj,
+ method_gnssIntervalGetEndMillisSinceGpsEpoch);
+ ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+ .startMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+ ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+ .endMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+ env->DeleteLocalRef(gnssIntervalObj);
+
+ env->DeleteLocalRef(gnssCorrectionComponentObj);
+ gnssSatelliteCorrections.ionosphericCorrections.push_back(ionosphericCorrection);
+ }
+ gnssSatelliteCorrectionsList.push_back(gnssSatelliteCorrections);
+ env->DeleteLocalRef(ionosphericCorrectionsObj);
+ }
+}
+
+void GnssAssistanceUtil::setRealTimeIntegrityModels(
+ JNIEnv* env, jobject realTimeIntegrityModelsObj,
+ std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels) {
+ if (realTimeIntegrityModelsObj == nullptr) return;
+ auto len = env->CallIntMethod(realTimeIntegrityModelsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject realTimeIntegrityModelObj =
+ env->CallObjectMethod(realTimeIntegrityModelsObj, method_listGet, i);
+ if (realTimeIntegrityModelObj == nullptr) continue;
+ RealTimeIntegrityModel realTimeIntegrityModel;
+ jint badSvid = env->CallIntMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetBadSvid);
+ jobject badSignalTypesObj =
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetBadSignalTypes);
+ auto badSignalTypesSize = env->CallIntMethod(badSignalTypesObj, method_listSize);
+ for (uint16_t j = 0; j < badSignalTypesSize; ++j) {
+ GnssSignalType badSignalType;
+ jobject badSignalTypeObj = env->CallObjectMethod(badSignalTypesObj, method_listGet, j);
+ if (badSignalTypeObj != nullptr) {
+ setGnssSignalType(env, badSignalTypeObj, badSignalType);
+ realTimeIntegrityModel.badSignalTypes.push_back(badSignalType);
+ env->DeleteLocalRef(badSignalTypeObj);
+ }
+ }
+
+ jlong startDateSeconds =
+ env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetStartDateSeconds);
+ jlong endDateSeconds = env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetEndDateSeconds);
+ jlong publishDateSeconds =
+ env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetPublishDateSeconds);
+ jstring advisoryNumber = static_cast<jstring>(
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetAdvisoryNumber));
+ ScopedJniString jniAdvisoryNumber{env, advisoryNumber};
+ jstring advisoryType = static_cast<jstring>(
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetAdvisoryType));
+ ScopedJniString jniAdvisoryType{env, advisoryType};
+
+ realTimeIntegrityModel.badSvid = badSvid;
+ realTimeIntegrityModel.startDateSeconds = startDateSeconds;
+ realTimeIntegrityModel.endDateSeconds = endDateSeconds;
+ realTimeIntegrityModel.publishDateSeconds = publishDateSeconds;
+ realTimeIntegrityModel.advisoryNumber = android::String16(jniAdvisoryNumber.c_str());
+ realTimeIntegrityModel.advisoryType = android::String16(jniAdvisoryType.c_str());
+ realTimeIntegrityModels.push_back(realTimeIntegrityModel);
+ env->DeleteLocalRef(badSignalTypesObj);
+ env->DeleteLocalRef(realTimeIntegrityModelObj);
+ }
+}
+
+void GnssAssistanceUtil::setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+ GnssSignalType& gnssSignalType) {
+ if (gnssSignalTypeObj == nullptr) {
+ ALOGE("gnssSignalTypeObj is null");
+ return;
+ }
+ jint constellationType =
+ env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetConstellationType);
+ jdouble carrierFrequencyHz =
+ env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCarrierFrequencyHz);
+ jstring codeType = static_cast<jstring>(
+ env->CallObjectMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCodeType));
+ ScopedJniString jniCodeType{env, codeType};
+
+ gnssSignalType.constellation = static_cast<GnssConstellationType>(constellationType);
+ gnssSignalType.carrierFrequencyHz = static_cast<int32_t>(carrierFrequencyHz);
+ gnssSignalType.codeType = std::string(jniCodeType.c_str());
+}
+
+void GnssAssistanceUtil::setTimeModels(JNIEnv* env, jobject timeModelsObj,
+ std::vector<TimeModel>& timeModels) {
+ if (timeModelsObj == nullptr) return;
+ auto len = env->CallIntMethod(timeModelsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject timeModelObj = env->CallObjectMethod(timeModelsObj, method_listGet, i);
+ TimeModel timeModel;
+ jint toGnss = env->CallIntMethod(timeModelObj, method_timeModelsGetToGnss);
+ jlong timeOfWeek = env->CallLongMethod(timeModelObj, method_timeModelsGetTimeOfWeek);
+ jint weekNumber = env->CallIntMethod(timeModelObj, method_timeModelsGetWeekNumber);
+ jdouble a0 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA0);
+ jdouble a1 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA1);
+ timeModel.toGnss = static_cast<GnssConstellationType>(toGnss);
+ timeModel.timeOfWeek = timeOfWeek;
+ timeModel.weekNumber = static_cast<int32_t>(weekNumber);
+ timeModel.a0 = a0;
+ timeModel.a1 = a1;
+ timeModels.push_back(timeModel);
+ env->DeleteLocalRef(timeModelObj);
+ }
+}
+
+void GnssAssistanceUtil::setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+ LeapSecondsModel& leapSecondsModel) {
+ if (leapSecondsModelObj == nullptr) {
+ leapSecondsModel.leapSeconds = -1;
+ return;
+ }
+ jint dayNumberLeapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj,
+ method_leapSecondsModelGetDayNumberLeapSecondsFuture);
+ jint leapSeconds =
+ env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSeconds);
+ jint leapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSecondsFuture);
+ jint weekNumberLeapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj,
+ method_leapSecondsModelGetWeekNumberLeapSecondsFuture);
+ leapSecondsModel.dayNumberLeapSecondsFuture = static_cast<int32_t>(dayNumberLeapSecondsFuture);
+ leapSecondsModel.leapSeconds = static_cast<int32_t>(leapSeconds);
+ leapSecondsModel.leapSecondsFuture = static_cast<int32_t>(leapSecondsFuture);
+ leapSecondsModel.weekNumberLeapSecondsFuture =
+ static_cast<int32_t>(weekNumberLeapSecondsFuture);
+}
+
+void GnssAssistanceUtil::setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+ SatelliteEphemerisTime& satelliteEphemerisTime) {
+ if (satelliteEphemerisTimeObj == nullptr) return;
+ jdouble iode =
+ env->CallDoubleMethod(satelliteEphemerisTimeObj, method_satelliteEphemerisTimeGetIode);
+ jdouble toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+ method_satelliteEphemerisTimeGetToeSeconds);
+ jint weekNumber = env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_satelliteEphemerisTimeGetWeekNumber);
+ satelliteEphemerisTime.iode = iode;
+ satelliteEphemerisTime.toeSeconds = toeSeconds;
+ satelliteEphemerisTime.weekNumber = weekNumber;
+}
+
+void GnssAssistanceUtil::setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+ KeplerianOrbitModel& keplerianOrbitModel) {
+ if (keplerianOrbitModelObj == nullptr) return;
+ jdouble rootA =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetRootA);
+ jdouble eccentricity = env->CallDoubleMethod(keplerianOrbitModelObj,
+ method_keplerianOrbitModelGetEccentricity);
+ jdouble m0 = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetM0);
+ jdouble omega =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmega);
+ jdouble omegaDot =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmegaDot);
+ jdouble deltaN =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetDeltaN);
+ jdouble iDot = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetIDot);
+ jobject secondOrderHarmonicPerturbationObj =
+ env->CallObjectMethod(keplerianOrbitModelObj,
+ method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation);
+ jdouble cic = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCic);
+ jdouble cis = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCis);
+ jdouble crs = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCrs);
+ jdouble crc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCrc);
+ jdouble cuc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCuc);
+ jdouble cus = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCus);
+ keplerianOrbitModel.rootA = rootA;
+ keplerianOrbitModel.eccentricity = eccentricity;
+ keplerianOrbitModel.m0 = m0;
+ keplerianOrbitModel.omega = omega;
+ keplerianOrbitModel.omegaDot = omegaDot;
+ keplerianOrbitModel.deltaN = deltaN;
+ keplerianOrbitModel.iDot = iDot;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cic = cic;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cis = cis;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.crs = crs;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.crc = crc;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cuc = cuc;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cus = cus;
+ env->DeleteLocalRef(secondOrderHarmonicPerturbationObj);
+}
+
+void GnssAssistanceUtil::setKlobucharIonosphericModel(
+ JNIEnv* env, jobject klobucharIonosphericModelObj,
+ KlobucharIonosphericModel& klobucharIonosphericModel) {
+ if (klobucharIonosphericModelObj == nullptr) return;
+ jdouble alpha0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha0);
+ jdouble alpha1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha1);
+ jdouble alpha2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha2);
+ jdouble alpha3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha3);
+ jdouble beta0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta0);
+ jdouble beta1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta1);
+ jdouble beta2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta2);
+ jdouble beta3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta3);
+ klobucharIonosphericModel.alpha0 = alpha0;
+ klobucharIonosphericModel.alpha1 = alpha1;
+ klobucharIonosphericModel.alpha2 = alpha2;
+ klobucharIonosphericModel.alpha3 = alpha3;
+ klobucharIonosphericModel.beta0 = beta0;
+ klobucharIonosphericModel.beta1 = beta1;
+ klobucharIonosphericModel.beta2 = beta2;
+ klobucharIonosphericModel.beta3 = beta3;
+}
+
+void GnssAssistanceUtil::setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel) {
+ if (utcModelObj == nullptr) {
+ utcModel.weekNumber = -1;
+ return;
+ }
+ jdouble a0 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA0);
+ jdouble a1 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA1);
+ jlong timeOfWeek = env->CallLongMethod(utcModelObj, method_utcModelGetTimeOfWeek);
+ jint weekNumber = env->CallIntMethod(utcModelObj, method_utcModelGetWeekNumber);
+ utcModel.a0 = a0;
+ utcModel.a1 = a1;
+ utcModel.timeOfWeek = timeOfWeek;
+ utcModel.weekNumber = static_cast<int32_t>(weekNumber);
+}
+
+void GnssAssistanceUtil::setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj,
+ GnssAlmanac& gnssAlmanac) {
+ if (gnssAlmanacObj == nullptr) {
+ gnssAlmanac.weekNumber = -1;
+ return;
+ }
+ jlong issueDateMillis =
+ env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetIssueDateMillis);
+ jint ioda = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetIoda);
+ jint weekNumber = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetWeekNumber);
+ jlong toaSeconds = env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetToaSeconds);
+ jboolean isCompleteAlmanacProvided =
+ env->CallBooleanMethod(gnssAlmanacObj, method_gnssAlmanacIsCompleteAlmanacProvided);
+ gnssAlmanac.issueDateMs = issueDateMillis;
+ gnssAlmanac.ioda = ioda;
+ gnssAlmanac.weekNumber = weekNumber;
+ gnssAlmanac.toaSeconds = toaSeconds;
+ gnssAlmanac.isCompleteAlmanacProvided = isCompleteAlmanacProvided;
+
+ jobject satelliteAlmanacsListObj =
+ env->CallObjectMethod(gnssAlmanacObj, method_gnssAlmanacGetSatelliteAlmanacs);
+ auto len = env->CallIntMethod(satelliteAlmanacsListObj, method_listSize);
+ std::vector<GnssSatelliteAlmanac> list(len);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject gnssSatelliteAlmanacObj =
+ env->CallObjectMethod(satelliteAlmanacsListObj, method_listGet, i);
+ if (gnssSatelliteAlmanacObj == nullptr) continue;
+ GnssSatelliteAlmanac gnssSatelliteAlmanac;
+ jint svid = env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvid);
+ jint svHealth =
+ env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvHealth);
+ jdouble af0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf0);
+ jdouble af1 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf1);
+ jdouble eccentricity = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+ method_satelliteAlmanacGetEccentricity);
+ jdouble inclination = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+ method_satelliteAlmanacGetInclination);
+ jdouble m0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetM0);
+ jdouble omega =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega);
+ jdouble omega0 =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega0);
+ jdouble omegaDot =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmegaDot);
+ jdouble rootA =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetRootA);
+ gnssSatelliteAlmanac.svid = static_cast<int32_t>(svid);
+ gnssSatelliteAlmanac.svHealth = static_cast<int32_t>(svHealth);
+ gnssSatelliteAlmanac.af0 = af0;
+ gnssSatelliteAlmanac.af1 = af1;
+ gnssSatelliteAlmanac.eccentricity = eccentricity;
+ gnssSatelliteAlmanac.inclination = inclination;
+ gnssSatelliteAlmanac.m0 = m0;
+ gnssSatelliteAlmanac.omega = omega;
+ gnssSatelliteAlmanac.omega0 = omega0;
+ gnssSatelliteAlmanac.omegaDot = omegaDot;
+ gnssSatelliteAlmanac.rootA = rootA;
+ list.at(i) = gnssSatelliteAlmanac;
+ env->DeleteLocalRef(gnssSatelliteAlmanacObj);
+ }
+ gnssAlmanac.satelliteAlmanacs = list;
+ env->DeleteLocalRef(satelliteAlmanacsListObj);
+}
+
+void GnssAssistanceUtil::setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+ AuxiliaryInformation& auxiliaryInformation) {
+ if (auxiliaryInformationObj == nullptr) {
+ auxiliaryInformation.svid = -1;
+ return;
+ }
+ jint svid = env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSvid);
+ jobject availableSignalTypesObj =
+ env->CallObjectMethod(auxiliaryInformationObj,
+ method_auxiliaryInformationGetAvailableSignalTypes);
+ auto size = env->CallIntMethod(availableSignalTypesObj, method_listSize);
+ std::vector<GnssSignalType> availableSignalTypes(size);
+ for (uint16_t i = 0; i < size; ++i) {
+ jobject availableSignalTypeObj =
+ env->CallObjectMethod(availableSignalTypesObj, method_listGet, i);
+ GnssSignalType availableSignalType;
+ setGnssSignalType(env, availableSignalTypeObj, availableSignalType);
+ availableSignalTypes.at(i) = availableSignalType;
+ env->DeleteLocalRef(availableSignalTypeObj);
+ }
+ jint frequencyChannelNumber =
+ env->CallIntMethod(auxiliaryInformationObj,
+ method_auxiliaryInformationGetFrequencyChannelNumber);
+ jint satType =
+ env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSatType);
+ auxiliaryInformation.svid = static_cast<int32_t>(svid);
+ auxiliaryInformation.availableSignalTypes = availableSignalTypes;
+ auxiliaryInformation.frequencyChannelNumber = static_cast<int32_t>(frequencyChannelNumber);
+ auxiliaryInformation.satType = static_cast<BeidouB1CSatelliteOrbitType>(satType);
+ env->DeleteLocalRef(availableSignalTypesObj);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistance.h b/services/core/jni/gnss/GnssAssistance.h
new file mode 100644
index 000000000000..ee97e19371f8
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSSASSITANCE_H
+#define _ANDROID_SERVER_GNSSASSITANCE_H
+
+#include <sys/stat.h>
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceInterface.h>
+#include <log/log.h>
+
+#include "GnssAssistanceCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+using IGnssAssistanceInterface = android::hardware::gnss::gnss_assistance::IGnssAssistanceInterface;
+using IGnssAssistanceCallback = android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
+using BeidouAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::BeidouAssistance;
+using BeidouSatelliteEphemeris = android::hardware::gnss::gnss_assistance::BeidouSatelliteEphemeris;
+using GalileoAssistance =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GalileoAssistance;
+using GalileoSatelliteEphemeris =
+ android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris;
+using GalileoIonosphericModel = android::hardware::gnss::gnss_assistance::GalileoIonosphericModel;
+using GlonassAssistance =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GlonassAssistance;
+using GlonassAlmanac = android::hardware::gnss::gnss_assistance::GlonassAlmanac;
+using GlonassSatelliteEphemeris =
+ android::hardware::gnss::gnss_assistance::GlonassSatelliteEphemeris;
+using GnssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
+using GpsAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::GpsAssistance;
+using QzssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::QzssAssistance;
+using GnssAlmanac = android::hardware::gnss::gnss_assistance::GnssAlmanac;
+using GnssSatelliteCorrections =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GnssSatelliteCorrections;
+using GpsSatelliteEphemeris = android::hardware::gnss::gnss_assistance::GpsSatelliteEphemeris;
+using SatelliteEphemerisTime = android::hardware::gnss::gnss_assistance::SatelliteEphemerisTime;
+using UtcModel = android::hardware::gnss::gnss_assistance::UtcModel;
+using LeapSecondsModel = android::hardware::gnss::gnss_assistance::LeapSecondsModel;
+using KeplerianOrbitModel = android::hardware::gnss::gnss_assistance::KeplerianOrbitModel;
+using KlobucharIonosphericModel =
+ android::hardware::gnss::gnss_assistance::KlobucharIonosphericModel;
+using TimeModel = android::hardware::gnss::gnss_assistance::TimeModel;
+using RealTimeIntegrityModel = android::hardware::gnss::gnss_assistance::RealTimeIntegrityModel;
+using AuxiliaryInformation = android::hardware::gnss::gnss_assistance::AuxiliaryInformation;
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssAssistanceInterface {
+public:
+ GnssAssistanceInterface(const sp<IGnssAssistanceInterface>& iGnssAssistance);
+ jboolean injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj);
+ jboolean setCallback(const sp<IGnssAssistanceCallback>& callback);
+
+private:
+ const sp<IGnssAssistanceInterface> mGnssAssistanceInterface;
+};
+
+struct GnssAssistanceUtil {
+ static void setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+ GlonassAssistance& galileoAssistance);
+ static void setGlonassSatelliteEphemeris(
+ JNIEnv* env, jobject glonassSatelliteEphemerisObj,
+ std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList);
+ static void setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+ GlonassAlmanac& glonassAlmanac);
+ static void setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+ BeidouAssistance& beidouAssistance);
+ static void setBeidouSatelliteEphemeris(
+ JNIEnv* env, jobject beidouSatelliteEphemerisObj,
+ std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList);
+ static void setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+ GalileoAssistance& galileoAssistance);
+ static void setGalileoSatelliteEphemeris(
+ JNIEnv* env, jobject galileoSatelliteEphemerisObj,
+ std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList);
+ static void setGaliloKlobucharIonosphericModel(JNIEnv* env, jobject galileoIonosphericModelObj,
+ GalileoIonosphericModel& ionosphericModel);
+ static void setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+ GnssAssistance& gnssAssistance);
+ static void setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+ GpsAssistance& gpsAssistance);
+ template <class T>
+ static void setGpsOrQzssSatelliteEphemeris(JNIEnv* env, jobject satelliteEphemerisObj,
+ std::vector<T>& satelliteEphemeris);
+ static void setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+ QzssAssistance& qzssAssistance);
+ static void setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj, GnssAlmanac& gnssAlmanac);
+ static void setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+ GnssSignalType& gnssSignalType);
+ static void setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+ KeplerianOrbitModel& keplerianOrbitModel);
+ static void setKlobucharIonosphericModel(JNIEnv* env, jobject klobucharIonosphericModelObj,
+ KlobucharIonosphericModel& klobucharIonosphericModel);
+ static void setTimeModels(JNIEnv* env, jobject timeModelsObj,
+ std::vector<TimeModel>& timeModels);
+ static void setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+ LeapSecondsModel& leapSecondsModel);
+ static void setRealTimeIntegrityModels(
+ JNIEnv* env, jobject realTimeIntegrityModelsObj,
+ std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels);
+
+ static void setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+ SatelliteEphemerisTime& satelliteEphemerisTime);
+ static void setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel);
+ static void setSatelliteCorrections(
+ JNIEnv* env, jobject satelliteCorrectionsObj,
+ std::vector<GnssSatelliteCorrections>& satelliteCorrections);
+ static void setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+ AuxiliaryInformation& auxiliaryInformation);
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSSASSITANCE__H
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.cpp b/services/core/jni/gnss/GnssAssistanceCallback.cpp
new file mode 100644
index 000000000000..dbb27d72663e
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GnssAssistanceCbJni"
+
+#include "GnssAssistanceCallback.h"
+
+#include "Utils.h"
+
+namespace {
+jmethodID method_gnssAssistanceInjectRequest;
+} // anonymous namespace
+
+namespace android::gnss {
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz) {
+ method_gnssAssistanceInjectRequest =
+ env->GetStaticMethodID(clazz, "gnssAssistanceInjectRequest", "()V");
+}
+
+// Implementation of android::hardware::gnss::gnss_assistance::GnssAssistanceCallback.
+
+Status GnssAssistanceCallback::injectRequestCb() {
+ ALOGD("%s.", __func__);
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_gnssAssistanceInjectRequest);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.h b/services/core/jni/gnss/GnssAssistanceCallback.h
new file mode 100644
index 000000000000..4c8c5fc06730
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * GnssAssistanceCallback class implements the callback methods required by the
+ * android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback interface.
+ */
+class GnssAssistanceCallback : public hardware::gnss::gnss_assistance::BnGnssAssistanceCallback {
+public:
+ GnssAssistanceCallback() {}
+ binder::Status injectRequestCb() override;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 42e457c97fd4..bc5c427e3ccb 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -51,7 +51,6 @@ import android.credentials.ISetEnabledProvidersCallback;
import android.credentials.PrepareGetCredentialResponseInternal;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.credentials.flags.Flags;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
@@ -538,34 +537,31 @@ public final class CredentialManagerService
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
- if (Flags.safeguardCandidateCredentialsApiCaller()) {
- try {
- String credentialManagerAutofillCompName = mContext.getResources().getString(
- R.string.config_defaultCredentialManagerAutofillService);
- ComponentName componentName = ComponentName.unflattenFromString(
- credentialManagerAutofillCompName);
- if (componentName == null) {
- throw new SecurityException(
- "Credential Autofill service does not exist on this device.");
- }
- PackageManager pm = mContext.createContextAsUser(
- UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
- String callingProcessPackage = pm.getNameForUid(callingUid);
- if (callingProcessPackage == null) {
- throw new SecurityException(
- "Couldn't determine the identity of the caller.");
- }
- if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
- throw new SecurityException(callingProcessPackage
- + " is not the device's credential autofill package.");
- }
- } catch (Resources.NotFoundException e) {
+ try {
+ String credentialManagerAutofillCompName = mContext.getResources().getString(
+ R.string.config_defaultCredentialManagerAutofillService);
+ ComponentName componentName = ComponentName.unflattenFromString(
+ credentialManagerAutofillCompName);
+ if (componentName == null) {
throw new SecurityException(
"Credential Autofill service does not exist on this device.");
}
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+ String callingProcessPackage = pm.getNameForUid(callingUid);
+ if (callingProcessPackage == null) {
+ throw new SecurityException(
+ "Couldn't determine the identity of the caller.");
+ }
+ if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
+ throw new SecurityException(callingProcessPackage
+ + " is not the device's credential autofill package.");
+ }
+ } catch (Resources.NotFoundException e) {
+ throw new SecurityException(
+ "Credential Autofill service does not exist on this device.");
}
-
// New request session, scoped for this request only.
final GetCandidateRequestSession session =
new GetCandidateRequestSession(
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index de78271acddc..69e856de401a 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -325,7 +325,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
}
if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
- if (Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
+ if (mLastHingeAngleSensorEvent != null
+ && Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
Trace.instant(TRACE_TAG_SYSTEM_SERVER,
"[Device state changed] Last hinge sensor event timestamp: "
+ mLastHingeAngleSensorEvent.timestamp);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index e545a49d3139..554b5b4297f2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -19,8 +19,8 @@ package com.android.server.pm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 9117cc8e5ab8..a38ecc8523b1 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -3186,6 +3186,32 @@ public class VpnTest extends VpnTestBase {
assertEquals(profile, ikev2VpnProfile.toVpnProfile());
}
+ @Test
+ public void testStartAlwaysOnVpnOnSafeMode() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ setMockedUsers(PRIMARY_USER);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn());
+ assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+
+ // Simulate safe mode and restart the always-on VPN to verify the always-on package is not
+ // reset.
+ doReturn(null).when(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ doReturn(null).when(mPackageManager).queryIntentServicesAsUser(
+ any(), any(), eq(PRIMARY_USER.id));
+ doReturn(true).when(mPackageManager).isSafeMode();
+ assertFalse(vpn.startAlwaysOnVpn());
+ assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+ }
+
// Make it public and un-final so as to spy it
public class TestDeps extends Vpn.Dependencies {
TestDeps() {}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 29f07227a12d..fecbc7c81347 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
@@ -396,7 +396,7 @@ public final class DisplayPowerProximityStateControllerTest {
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
- verifyZeroInteractions(mWakelockController);
+ verifyNoMoreInteractions(mWakelockController);
}
private void setScreenOffBecauseOfPositiveProximityState() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f8b4113a3c04..b8a5f341eb8a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -234,6 +234,7 @@ public class LocalDisplayAdapterTest {
doReturn(true).when(mFlags).isDisplayOffloadEnabled();
doReturn(true).when(mFlags).isEvenDimmerEnabled();
+ doReturn(true).when(mFlags).isDisplayContentModeManagementEnabled();
initDisplayOffloadSession();
}
@@ -1468,6 +1469,103 @@ public class LocalDisplayAdapterTest {
assertFalse(mDisplayOffloadSession.isActive());
}
+ @Test
+ public void testAllowsContentSwitch_firstDisplay() throws Exception {
+ // Set up a first display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The first display should be allowed to use the content mode switch
+ DisplayDevice firstDisplayDevice = mListener.addedDisplays.get(0);
+ assertTrue((firstDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_secondaryDisplayPublicAndNotShouldShowOwnContent()
+ throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a public display
+ doReturn(new int[0]).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ // Disable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(true).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // This secondary display should be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_privateDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a private display
+ doReturn(new int[]{ PORT_B }).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The private display should not be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_ownContentOnlyDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Enable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(false).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The secondary display with FLAG_OWN_CONTENT_ONLY enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_flagShouldShowSystemDecorations() throws Exception {
+ // Set up a display
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+ int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+ boolean allowsContentModeSwitch =
+ ((flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ boolean shouldShowSystemDecorations =
+ ((flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0);
+ assertFalse(allowsContentModeSwitch && shouldShowSystemDecorations);
+ }
+
private void initDisplayOffloadSession() {
when(mDisplayOffloader.startOffload()).thenReturn(true);
when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index 019b70ef1424..f067fa10f611 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.display.DisplayManagerInternal;
@@ -210,7 +210,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityPositiveAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -238,7 +238,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityNegativeAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -265,7 +265,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isOnStateChangedPending());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
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 49de80179683..bf0543939d85 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
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -422,7 +421,7 @@ public final class DisplayBrightnessControllerTest {
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
- verifyZeroInteractions(automaticBrightnessController);
+ verifyNoMoreInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index f9fbf1bd5085..b41f03bb8030 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -17,9 +17,9 @@
package com.android.server.display.whitebalance;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
index 7c239ef02e58..586ff52aa78c 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
@@ -328,6 +328,7 @@ public class TestDreamEnvironment {
case DREAM_STATE_STARTED -> startDream();
case DREAM_STATE_WOKEN -> wakeDream();
}
+ mTestableLooper.processAllMessages();
} while (mCurrentDreamState < state);
return true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index eda5e8613dba..77d67019c0ed 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -67,6 +67,9 @@ import java.util.concurrent.TimeUnit;
/**
* Test RescueParty.
+ * TODO: b/354112511 delete this file
+ * Moved to frameworks/base/tests/PackageWatchdog/src/com/android/server/RescuePartyTest
+ *
*/
public class RescuePartyTest {
private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d79d88400cf9..f0e61ec9c692 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,7 +109,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -3724,8 +3724,8 @@ public final class AlarmManagerServiceTest {
setDeviceConfigInt(KEY_TEMPORARY_QUOTA_BUMP, 0);
mAppStandbyListener.triggerTemporaryQuotaBump(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- verifyZeroInteractions(mPackageManagerInternal);
- verifyZeroInteractions(mService.mHandler);
+ verifyNoMoreInteractions(mPackageManagerInternal);
+ verifyNoMoreInteractions(mService.mHandler);
}
private void testTemporaryQuota_bumpedAfterDeferral(int standbyBucket) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 7dab1c854625..859d2d2f2e38 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -244,7 +244,7 @@ public class AlarmStoreTest {
addAlarmsToStore(simpleAlarm, alarmClock);
mAlarmStore.remove(simpleAlarm::equals);
- verifyZeroInteractions(onRemoved);
+ verifyNoMoreInteractions(onRemoved);
mAlarmStore.remove(alarmClock::equals);
verify(onRemoved).run();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 35ab2d233563..acc06d0c7cba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -74,7 +74,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -584,7 +583,7 @@ public class ActivityManagerServiceTest {
if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
} else {
- verifyZeroInteractions(app.getThread());
+ verifyNoMoreInteractions(app.getThread());
}
Mockito.reset(app.getThread());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 194bf4ba73f3..84110ae5cd02 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -34,9 +34,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e678acc092e9..987b9c6427d8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -21,6 +21,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.am.ActivityManagerService.Injector;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -625,6 +626,60 @@ public class ApplicationStartInfoTest {
assertTrue(startInfo.equals(startInfoFromParcel));
}
+ /** Test that new timestamps are added to the correct record (the most recently created one). */
+ @Test
+ public void testTimestampAddedToCorrectRecord() throws Exception {
+ // Use a different start timestamp for each record so we can identify which was added to.
+ final long startTimeRecord1 = 123L;
+ final long startTimeRecord2 = 456L;
+
+ final long forkTime = 789L;
+
+ // Create a process record to use with all starts.
+ ProcessRecord app = makeProcessRecord(
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ null, // definingUid
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
+
+ // Trigger a start info record.
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Wait at least 1 ms for monotonic time to increase.
+ sleep(1);
+
+ // Verify the record was added successfully.
+ ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(1, list.size());
+ assertEquals(startTimeRecord1, list.get(0).getStartupTimestamps().get(0).longValue());
+
+ // Now trigger another start info record.
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Add a timestamp to the most recent record.
+ mAppStartInfoTracker.addTimestampToStart(
+ app, forkTime, ApplicationStartInfo.START_TIMESTAMP_FORK);
+
+ // Verify the record was added successfully.
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(2, list.size());
+ assertEquals(startTimeRecord2, list.get(0).getStartupTimestamps().get(0).longValue());
+ assertEquals(startTimeRecord1, list.get(1).getStartupTimestamps().get(0).longValue());
+
+ // Verify that the new timestamp is set correctly on the 2nd record that was added and not
+ // on the first.
+ assertEquals(forkTime, list.get(0).getStartupTimestamps()
+ .get(ApplicationStartInfo.START_TIMESTAMP_FORK).longValue());
+ assertFalse(list.get(1).getStartupTimestamps().containsKey(
+ ApplicationStartInfo.START_TIMESTAMP_FORK));
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 637c73f31ce0..dd5924ad4a7e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -88,7 +88,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
@@ -747,7 +747,7 @@ public final class BackgroundRestrictionTest {
mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
- doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+ doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(any());
mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid,
testPid, true);
mAppFGSTracker.onForegroundServiceNotificationUpdated(
@@ -1925,7 +1925,7 @@ public final class BackgroundRestrictionTest {
mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
- doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+ doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(any());
// Run with a media playback service which starts/stops immediately, we should
// goto the restricted bucket.
@@ -3170,7 +3170,7 @@ public final class BackgroundRestrictionTest {
inv.getArgument(2));
return null;
}).when(telephonyManager).registerCarrierPrivilegesCallback(
- anyInt(), anyObject(), anyObject());
+ anyInt(), any(), any());
}
public void registerCarrierPrivilegesCallback(int phoneId, Executor executor,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 3a9c99d57d71..d540b2ec13eb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -155,7 +155,6 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
- assertHealth();
final String processName = invocation.getArgument(0);
final ProcessStartBehavior behavior = mNewProcessStartBehaviors.getOrDefault(
processName, mNextProcessStartBehavior.getAndSet(ProcessStartBehavior.SUCCESS));
@@ -206,6 +205,9 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
mActiveProcesses.remove(res);
res.setKilled(true);
break;
+ case MISSING_RESPONSE:
+ res.setPendingStart(true);
+ break;
default:
throw new UnsupportedOperationException();
}
@@ -244,6 +246,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
mConstants.MAX_FROZEN_OUTGOING_BROADCASTS = 10;
+ mConstants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS = 2000;
}
@After
@@ -279,6 +282,8 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
FAIL_NULL,
/** Process is killed without reporting to BroadcastQueue */
KILLED_WITHOUT_NOTIFY,
+ /** Process start fails without no response */
+ MISSING_RESPONSE,
}
private enum ProcessBehavior {
@@ -1173,6 +1178,37 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
}
+ @Test
+ public void testProcessStartWithMissingResponse() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+
+ mNewProcessStartBehaviors.put(PACKAGE_GREEN, ProcessStartBehavior.MISSING_RESPONSE);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
+ withPriority(makeRegisteredReceiver(receiverBlueApp), 5),
+ withPriority(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), 0))));
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE))));
+
+ waitForIdle();
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW));
+ final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
+ getUidForPackage(PACKAGE_ORANGE));
+
+ verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
+ verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
+ verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
+ verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
+ }
+
/**
* Verify that a broadcast sent to a frozen app, which gets killed as part of unfreezing
* process due to pending sync binder transactions, is delivered as expected.
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
index ae0452a60161..b7087c74bf8d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
@@ -105,7 +105,7 @@ public class FullBackupUtilsTest {
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -126,7 +126,7 @@ public class FullBackupUtilsTest {
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -141,7 +141,7 @@ public class FullBackupUtilsTest {
FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index bf7e3a0bd0a6..346d5f787621 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -32,7 +32,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.backup.IBackupManagerMonitor;
@@ -239,7 +238,7 @@ public class TarBackupReaderTest {
mMockPackageManagerInternal, mUserId, mContext);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
- verifyZeroInteractions(mBackupManagerMonitorMock);
+ verifyNoMoreInteractions(mBackupManagerMonitorMock);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 3e8794377d37..2d84887afb41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1029,6 +1029,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1127,6 +1128,54 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 5;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ // There is only one session in the past active bucket window, the empty time for this
+ // window is the bucket window size - duration of the session.
+ expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
*/
@@ -1195,6 +1244,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
// Set time to 3 minutes after boot.
advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
@@ -1206,10 +1256,10 @@ public class QuotaControllerTest {
ExecutionStats expectedStats = new ExecutionStats();
// Exempted
- expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
- expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
- expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 2;
@@ -1268,6 +1318,49 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 2;
+ expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 2;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
*/
@@ -1425,6 +1518,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
for (int i = 0; i < 20; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1581,6 +1675,165 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() {
+ for (int i = 0; i < 20; ++i) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 5 * MINUTE_IN_MILLIS, 5), false);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ for (int j = 0; j < 5; ++j) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ MINUTE_IN_MILLIS, 2), false);
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ advanceElapsedClock(54 * SECOND_IN_MILLIS);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+ advanceElapsedClock(500);
+ advanceElapsedClock(400);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+ advanceElapsedClock(100);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ }
+ advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(16, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(64, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(192, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(320, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * SECOND_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(7, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+ assertEquals(28, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+ assertEquals(84, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+ assertEquals(140, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ MINUTE_IN_MILLIS);
+
+ // Only two TimingSessions there for every hour.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(24, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(40, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * MINUTE_IN_MILLIS);
+
+ // Only one TimingSessions there for every hour
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 15 * MINUTE_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+ // between an hour and 15 minutes.
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
*/
@@ -2231,32 +2484,6 @@ public class QuotaControllerTest {
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window.
@@ -2327,6 +2554,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Close to RARE boundary.
@@ -2390,7 +2618,30 @@ public class QuotaControllerTest {
@Test
@EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to ACTIVE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+
+ // ACTIVE window != allowed time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(17 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(20 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_Installer() {
PackageInfo pi = new PackageInfo();
pi.packageName = SOURCE_PACKAGE;
@@ -2412,7 +2663,7 @@ public class QuotaControllerTest {
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2422,12 +2673,12 @@ public class QuotaControllerTest {
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Close to EXEMPTED boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// No additional quota for the system installer when the app is in RARE, FREQUENT,
@@ -2486,6 +2737,42 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = SOURCE_PACKAGE;
+ pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
+ pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = mSourceUid;
+ doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
+ eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
+ mQuotaController.onSystemServicesReady();
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to EXEMPTED boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), false);
+
+ // Additional quota for the system installer when the app is in EXEMPTED bucket.
+ // EXEMPTED window == allowed time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(38 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
*/
@@ -2637,33 +2924,6 @@ public class QuotaControllerTest {
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS),
- mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
- false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size != allowed time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(0,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window and the session is rolling out of the window.
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 3d0c63780ef3..29af7d28339d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -27,7 +27,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
@@ -128,7 +128,7 @@ public class BackgroundUserSoundNotifierTest {
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
@@ -143,7 +143,7 @@ public class BackgroundUserSoundNotifierTest {
Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index 8257168f8d08..8257168f8d08 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index dc04b6aea318..bf3fe8c70bf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AppOpsManager;
import android.content.Context;
@@ -176,9 +176,9 @@ public class CameraPrivacyLightControllerTest {
prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
- verifyZeroInteractions(mLightsManager);
- verifyZeroInteractions(mAppOpsManager);
- verifyZeroInteractions(mSensorManager);
+ verifyNoMoreInteractions(mLightsManager);
+ verifyNoMoreInteractions(mAppOpsManager);
+ verifyNoMoreInteractions(mSensorManager);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
index 52cd29cabb94..1415dd690a0a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
@@ -22,10 +22,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.contains;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.matches;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 241ffdc19ce4..4f74667f3ff2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -33,10 +33,8 @@ import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -48,7 +46,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -95,7 +92,6 @@ public class WallpaperCropperTest {
@Mock
private Resources mResources;
- private WallpaperCropper mWallpaperCropper;
private static final Point PORTRAIT_ONE = new Point(500, 800);
private static final Point PORTRAIT_TWO = new Point(400, 1000);
@@ -171,7 +167,6 @@ public class WallpaperCropperTest {
return getWallpaperTestDir(userId);
}).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
- mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
}
private File getWallpaperTestDir(int userId) {
@@ -738,13 +733,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ DEFAULT_DISPLAY, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
@@ -755,49 +750,67 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
// aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeLargerThanDisplayAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(4000, 3000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
- // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
- // aspect ratio meets the hard-coded aspect ratio.
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButBeyondThresholdAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
- new Point(1000, 500));
+ new Point(2000, 900));
+
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButAboveThresholdAndMeetAspectRatio_returnFalse()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(2000, 800));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
@@ -809,13 +822,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
@@ -827,24 +840,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1044;
displayInfo.logicalHeight = 2560;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
- }
-
- private void mockDisplay(int displayId, Point displayResolution) {
- final Display mockDisplay = mock(Display.class);
- when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
- DisplayInfo displayInfo = invocation.getArgument(0);
- displayInfo.displayId = displayId;
- displayInfo.logicalWidth = displayResolution.x;
- displayInfo.logicalHeight = displayResolution.y;
- return true;
- });
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
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 4e56422ec391..94ce72368a92 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -39,9 +39,9 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.content.Context;
@@ -53,8 +53,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -205,7 +205,7 @@ public class NotifierTest {
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -238,7 +238,7 @@ public class NotifierTest {
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -725,10 +725,11 @@ public class NotifierTest {
final int uid = 1234;
final int pid = 5678;
+
mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
- verifyZeroInteractions(mWakeLockLog);
+ verifyNoMoreInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
clearInvocations(mBatteryStats);
@@ -790,6 +791,55 @@ public class NotifierTest {
}
@Test
+ public void test_wakeLockLogUsesWorkSource() {
+ createNotifier();
+ clearInvocations(mWakeLockLog);
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+
+ final int uid = 1234;
+ final int pid = 5678;
+ WorkSource worksource = new WorkSource(1212);
+ WorkSource worksource2 = new WorkSource(3131);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);
+
+ // clear the handler
+ mTestLooper.dispatchAll();
+
+ // Now test with improveWakelockLatency flag true
+ clearInvocations(mWakeLockLog);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, 1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
+ }
+
+ @Test
public void
test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
@@ -845,7 +895,7 @@ public class NotifierTest {
exceptingCallback);
// No interaction because we expect that to happen in async
- verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
// Progressing the looper, and validating all the interactions
mTestLooper.dispatchAll();
@@ -944,15 +994,23 @@ public class NotifierTest {
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.DOZE_WAKE_LOCK), -1);
+ }
+
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
+ // How proximity wakelock is evaluated depends on boolean configuration. Test both.
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+ .thenReturn(false);
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
- assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
- PowerManager.DOZE_WAKE_LOCK), -1);
when(mResourcesSpy.getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
.thenReturn(true);
-
createNotifier();
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1239,7 +1297,7 @@ public class NotifierTest {
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index c1d7c7b4a4c2..534337ee9dbf 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -27,6 +27,8 @@ import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.os.Process;
+import com.android.server.power.WakeLockLog.TagData;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -62,8 +64,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItems_withNoEventTimeSupplied() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItemsWithTimeReset() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItemsWithTagOverwrite() {
- final int tagDatabaseSize = 2;
+ final int tagDatabaseSize = 1;
+ final int tagStartingSize = 1;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@ public class WakeLockLogTest {
@Test
public void testAddFourItemsWithRingBufferOverflow() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithBadTag() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Bad tag means it wont get written
@@ -224,8 +232,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithReducedTagName() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@ public class WakeLockLogTest {
@Test
public void testAddAcquireAndReleaseWithRepeatTagName() {
- final int tagDatabaseSize = 6;
+ final int tagDatabaseSize = 5;
+ final int tagStartingSize = 5;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@ public class WakeLockLogTest {
@Test
public void testAddAcquireAndReleaseWithTimeTravel() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@ public class WakeLockLogTest {
@Test
public void testAddSystemWakelock() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithNoPackageName() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithMultiplePackageNames() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemsWithRepeatOwnerUid_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@ public class WakeLockLogTest {
verify(mPackageManager, times(1)).getPackagesForUid(101);
}
+ @Test
+ public void testTagDatabaseGrowsBeyondStartingSize() {
+ final int tagDatabaseSize = 3;
+ final int tagStartingSize = 1;
+ final int logSize = 10;
+ // start with size = 1 and max size
+ TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
+ WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);
+
+ // Add one
+ TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data1));
+
+ // Check that it grows by adding 1 more
+ TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
+ assertEquals(1, td.getTagIndex(data2));
+
+ // Lets add the last one to fill up the DB to maxSize
+ TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
+ assertEquals(2, td.getTagIndex(data3));
+
+ // Adding a fourth one should replace the oldest one (Tagname1)
+ TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data4));
+ assertEquals(tagDatabaseSize, td.getTagIndex(data1));
+
+ }
+
private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@ public class WakeLockLogTest {
public static class TestInjector extends WakeLockLog.Injector {
private final int mTagDatabaseSize;
+ private final int mTagStartingSize;
private final int mLogSize;
- public TestInjector(int tagDatabaseSize, int logSize) {
+ public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
mTagDatabaseSize = tagDatabaseSize;
+ mTagStartingSize = tagStartingSize;
mLogSize = logSize;
}
@@ -442,6 +488,11 @@ public class WakeLockLogTest {
}
@Override
+ public int getTagDatabaseStartingSize() {
+ return mTagStartingSize;
+ }
+
+ @Override
public int getLogSize() {
return mLogSize;
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 7157d56db345..8a1d37b55255 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -574,7 +574,7 @@ public class BatteryUsageStatsProviderTest {
MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
accumulateBatteryUsageStats(batteryStats, 10000000, 0);
// Accumulate every 200 bytes of battery history
- accumulateBatteryUsageStats(batteryStats, 200, 2);
+ accumulateBatteryUsageStats(batteryStats, 200, 1);
accumulateBatteryUsageStats(batteryStats, 50, 5);
// Accumulate on every invocation of accumulateBatteryUsageStats
accumulateBatteryUsageStats(batteryStats, 0, 7);
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index 879aa4893802..2fd316edf71a 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -409,7 +409,7 @@ public class IntrusionDetectionServiceTest {
final String TAG = "startTestService";
final CountDownLatch latch = new CountDownLatch(1);
LocalIntrusionDetectionEventTransport transport =
- new LocalIntrusionDetectionEventTransport();
+ new LocalIntrusionDetectionEventTransport(mContext);
ServiceConnection serviceConnection = new ServiceConnection() {
// Called when connection with the service is established.
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
index f0012da44fa4..b0b781575cb3 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
@@ -18,8 +18,13 @@
package com.android.coretests.apps.testapp;
+import android.app.admin.SecurityLog;
+import android.app.admin.SecurityLog.SecurityEvent;
+import android.content.Context;
+import android.content.Intent;
import android.security.intrusiondetection.IntrusionDetectionEvent;
import android.security.intrusiondetection.IntrusionDetectionEventTransport;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +41,44 @@ import java.util.List;
public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEventTransport {
private List<IntrusionDetectionEvent> mEvents = new ArrayList<>();
+ private static final String ACTION_SECURITY_EVENT_RECEIVED =
+ "com.android.coretests.apps.testapp.ACTION_SECURITY_EVENT_RECEIVED";
+ private static final String TAG = "LocalIntrusionDetectionEventTransport";
+ private static final String TEST_SECURITY_EVENT_TAG = "test_security_event_tag";
+ private static Context sContext;
+
+ public LocalIntrusionDetectionEventTransport(Context context) {
+ sContext = context;
+ }
+
+ // Broadcast an intent to the CTS test service to indicate that the security
+ // event was received.
+ private static void broadcastSecurityEventReceived() {
+ try {
+ Intent intent = new Intent(ACTION_SECURITY_EVENT_RECEIVED);
+ sContext.sendBroadcast(intent);
+ Log.i(TAG, "LIZ_TESTING: sent broadcast");
+ } catch (Exception e) {
+ Log.e(TAG, "Exception sending broadcast", e);
+ }
+ }
+
+ private static void checkIfSecurityEventReceivedFromCts(List<IntrusionDetectionEvent> events) {
+ // Loop through the events and check if any of them are the security event
+ // that uses the TEST_SECURITY_EVENT_TAG tag, which is set by the CTS test.
+ for (IntrusionDetectionEvent event : events) {
+ if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) {
+ SecurityEvent securityEvent = event.getSecurityEvent();
+ Object[] eventData = (Object[]) securityEvent.getData();
+ if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED
+ && eventData[1].equals(TEST_SECURITY_EVENT_TAG)) {
+ broadcastSecurityEventReceived();
+ return;
+ }
+ }
+ }
+ }
+
@Override
public boolean initialize() {
return true;
@@ -43,6 +86,11 @@ public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEve
@Override
public boolean addData(List<IntrusionDetectionEvent> events) {
+ // Our CTS tests will generate a security event. In order to
+ // verify the event is received with the appropriate data, we will
+ // check the events locally and set a property value that can be
+ // read by the test.
+ checkIfSecurityEventReceivedFromCts(events);
mEvents.addAll(events);
return true;
}
@@ -55,4 +103,4 @@ public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEve
public List<IntrusionDetectionEvent> getEvents() {
return mEvents;
}
-} \ No newline at end of file
+}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
index e4bf987402fd..9183a75580ff 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
@@ -17,19 +17,20 @@
package com.android.coretests.apps.testapp;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
-import android.os.Process;
-
-import com.android.internal.infra.AndroidFuture;
-
public class TestLoggingService extends Service {
private static final String TAG = "TestLoggingService";
private LocalIntrusionDetectionEventTransport mLocalIntrusionDetectionEventTransport;
- public TestLoggingService() {
- mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport();
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Context context = getApplicationContext();
+ mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport(context);
}
// Binder given to clients.
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 5240f581fd9f..cc0d5e4710d2 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -32,8 +32,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index ce3751abfed7..d254e9689048 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -23,10 +23,10 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.Manifest.permission;
@@ -243,7 +242,7 @@ public class NetworkScoreServiceTest {
@Test
public void testRequestScores_providerNotConnected() throws Exception {
assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
- verifyZeroInteractions(mRecommendationProvider);
+ verifyNoMoreInteractions(mRecommendationProvider);
}
@Test
@@ -328,8 +327,8 @@ public class NetworkScoreServiceTest {
// updateScores should update both caches
mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
- verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
- verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
+ verify(mNetworkScoreCache).updateScores(anyList());
+ verify(mNetworkScoreCache2).updateScores(anyList());
mNetworkScoreService.unregisterNetworkScoreCache(
NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
@@ -337,7 +336,7 @@ public class NetworkScoreServiceTest {
// updateScores should only update the first cache since the 2nd has been unregistered
mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
- verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
+ verify(mNetworkScoreCache, times(2)).updateScores(anyList());
mNetworkScoreService.unregisterNetworkScoreCache(
NetworkKey.TYPE_WIFI, mNetworkScoreCache);
@@ -604,7 +603,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, null /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -618,7 +617,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -632,7 +631,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -646,7 +645,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -658,7 +657,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
- verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -676,7 +675,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mScanResultsFilter);
+ verifyNoMoreInteractions(mScanResultsFilter);
}
@Test
@@ -694,7 +693,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mCurrentNetworkFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 52428e8bc478..1621c5d41a3d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -23,9 +23,9 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index c18faef2c028..16df97b76c94 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -18,7 +18,7 @@ package com.android.server;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 42b84bdc51e6..c7c8c5846bb1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -64,7 +64,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
@@ -838,7 +837,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...without secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -856,7 +855,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -889,7 +888,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
private void takeScreenshotOfWindow(int windowFlags) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 8253595a50d1..2ccd33648c3e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -50,6 +50,7 @@ import static org.junit.Assume.assumeTrue;
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;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -66,6 +67,7 @@ import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -77,10 +79,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
@@ -114,6 +118,7 @@ import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.TestUtils;
import com.android.internal.R;
@@ -136,6 +141,8 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Correspondence;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -188,6 +195,8 @@ public class AccessibilityManagerServiceTest {
DESCRIPTION,
TEST_PENDING_INTENT);
+ private static final int FAKE_SYSTEMUI_UID = 1000;
+
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
private static final String TARGET_MAGNIFICATION = MAGNIFICATION_CONTROLLER_NAME;
private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
@@ -207,11 +216,12 @@ public class AccessibilityManagerServiceTest {
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
@Mock private PackageManager mMockPackageManager;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
@Mock private WindowManagerInternal mMockWindowManagerService;
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
- @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
@Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@@ -234,6 +244,7 @@ public class AccessibilityManagerServiceTest {
private TestableLooper mTestableLooper;
private Handler mHandler;
private FakePermissionEnforcer mFakePermissionEnforcer;
+ private TestDisplayManagerWrapper mTestDisplayManagerWrapper;
@Before
public void setUp() throws Exception {
@@ -246,6 +257,7 @@ public class AccessibilityManagerServiceTest {
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(PermissionEnforcer.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(
WindowManagerInternal.class, mMockWindowManagerService);
LocalServices.addService(
@@ -256,6 +268,12 @@ public class AccessibilityManagerServiceTest {
mInputFilter = mock(FakeInputFilter.class);
mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+ new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
+ when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
+ anyInt())).thenReturn(FAKE_SYSTEMUI_UID);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+
when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
@@ -273,15 +291,9 @@ public class AccessibilityManagerServiceTest {
eq(UserHandle.USER_CURRENT)))
.thenReturn(mTestableContext.getUserId());
- final ArrayList<Display> displays = new ArrayList<>();
- final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
- Display.DEFAULT_DISPLAY, new DisplayInfo(),
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
- new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- displays.add(defaultDisplay);
- displays.add(testDisplay);
- when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+ mTestDisplayManagerWrapper = new TestDisplayManagerWrapper(mTestableContext);
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
mA11yms = new AccessibilityManagerService(
mTestableContext,
@@ -290,7 +302,7 @@ public class AccessibilityManagerServiceTest {
mMockSecurityPolicy,
mMockSystemActionPerformer,
mMockA11yWindowManager,
- mMockA11yDisplayListener,
+ mTestDisplayManagerWrapper,
mMockMagnificationController,
mInputFilter,
mProxyManager,
@@ -2309,6 +2321,73 @@ public class AccessibilityManagerServiceTest {
mA11yms.getCurrentUserIdLocked())).isEmpty();
}
+ @Test
+ public void displayListReturnsDisplays() {
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(
+ Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL
+ );
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // In #setUp() we already have TYPE_INTERNAL and TYPE_EXTERNAL. Call the rest.
+ for (int i = 2; i < mTestDisplayManagerWrapper.mDisplays.size(); i++) {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(
+ mTestDisplayManagerWrapper.mDisplays.get(i).getDisplayId());
+ }
+ });
+
+ List<Display> displays = mA11yms.getValidDisplayList();
+ assertThat(displays).hasSize(5);
+ assertThat(displays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .containsExactly(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_excludesVirtualPrivate() {
+ // Add a private virtual display whose uid is different from systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID + 100));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(2);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .doesNotContain(Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_includesVirtualSystemUIPrivate() {
+ // Add a private virtual display whose uid is systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(3);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .contains(Display.TYPE_VIRTUAL);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(
@@ -2422,6 +2501,27 @@ public class AccessibilityManagerServiceTest {
});
}
+ private static List<Display> createFakeDisplayList(int... types) {
+ final ArrayList<Display> displays = new ArrayList<>();
+ for (int i = 0; i < types.length; i++) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = types[i];
+ final Display display = new Display(DisplayManagerGlobal.getInstance(),
+ i, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ displays.add(display);
+ }
+ return displays;
+ }
+
+ private static Display createFakeVirtualPrivateDisplay(int displayId, int uid) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = Display.TYPE_VIRTUAL;
+ info.flags |= Display.FLAG_PRIVATE;
+ info.ownerUid = uid;
+ return new Display(DisplayManagerGlobal.getInstance(),
+ displayId, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
public static class FakeInputFilter extends AccessibilityInputFilter {
FakeInputFilter(Context context,
AccessibilityManagerService service) {
@@ -2506,4 +2606,35 @@ public class AccessibilityManagerServiceTest {
Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
assertThat(setting).containsExactlyElementsIn(value);
}
+
+ private static class TestDisplayManagerWrapper extends
+ AccessibilityDisplayListener.DisplayManagerWrapper {
+ List<Display> mDisplays;
+ DisplayManager.DisplayListener mRegisteredListener;
+
+ TestDisplayManagerWrapper(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Display[] getDisplays() {
+ return mDisplays.toArray(new Display[0]);
+ }
+
+ @Override
+ public Display getDisplay(int displayId) {
+ for (final Display display : mDisplays) {
+ if (display.getDisplayId() == displayId) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mRegisteredListener = listener;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
index c16f3d3fa1ab..1792201a304a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
@@ -29,9 +29,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -129,7 +129,7 @@ public class ActionReplacingCallbackTest {
mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
INTERROGATING_TID);
verify(mMockReplacerConnection).findAccessibilityNodeInfoByAccessibilityId(
- eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(),
+ eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) any(),
mInteractionIdCaptor.capture(), eq(mActionReplacingCallback), eq(0),
eq(INTERROGATING_PID), eq(INTERROGATING_TID), nullable(MagnificationSpec.class),
nullable(float[].class), eq(null));
@@ -246,9 +246,9 @@ public class ActionReplacingCallbackTest {
throws RemoteException {
doThrow(RemoteException.class).when(mMockReplacerConnection)
.findAccessibilityNodeInfoByAccessibilityId(eq(AccessibilityNodeInfo.ROOT_NODE_ID),
- (Region) anyObject(), anyInt(), (ActionReplacingCallback) anyObject(),
+ (Region) any(), anyInt(), (ActionReplacingCallback) any(),
eq(0), eq(INTERROGATING_PID), eq(INTERROGATING_TID),
- (MagnificationSpec) anyObject(), nullable(float[].class), eq(null));
+ (MagnificationSpec) any(), nullable(float[].class), eq(null));
ActionReplacingCallback actionReplacingCallback = new ActionReplacingCallback(
mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
INTERROGATING_TID);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 96ae102e53f3..d0dc2cbb0f86 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.FingerprintGestureController;
@@ -86,7 +86,7 @@ public class FingerprintGestureControllerTest {
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -118,7 +118,7 @@ public class FingerprintGestureControllerTest {
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
@@ -135,7 +135,7 @@ public class FingerprintGestureControllerTest {
mFingerprintGestureController.unregisterFingerprintGestureCallback(
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -159,7 +159,7 @@ public class FingerprintGestureControllerTest {
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index 30d00ad716e6..f5a708d71ba3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -18,8 +18,8 @@ package com.android.server.accessibility;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 63c572af37b2..3565244d90b3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -36,6 +36,8 @@ import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -67,6 +69,8 @@ import java.util.concurrent.Executor;
public class HearingDevicePhoneCallNotificationControllerTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
@@ -118,6 +122,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -132,6 +137,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -146,6 +152,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -155,6 +162,51 @@ public class HearingDevicePhoneCallNotificationControllerTest {
eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
}
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ verify(mAudioManager).removeOnCommunicationDeviceChangedListener(
+ eq(listenerCaptor.getValue()));
+ }
+
+
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLE_HEADSET);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{hapDeviceInfo});
+ listenerCaptor.getValue().onCommunicationDeviceChanged(hapDeviceInfo);
+
+ verify(mNotificationManager).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+ }
+
private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index ebb73e877db4..186f7425b189 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -20,14 +20,14 @@ import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
@@ -96,12 +96,12 @@ public class KeyEventDispatcherTest {
mLock, powerManager, mMessageCapturingHandler);
mKeyEventFilter1 = mock(KeyEventFilter.class);
- when(mKeyEventFilter1.onKeyEvent((KeyEvent) anyObject(),
+ when(mKeyEventFilter1.onKeyEvent((KeyEvent) any(),
mFilter1SequenceCaptor.capture().intValue()))
.thenReturn(true);
mKeyEventFilter2 = mock(KeyEventFilter.class);
- when(mKeyEventFilter2.onKeyEvent((KeyEvent) anyObject(),
+ when(mKeyEventFilter2.onKeyEvent((KeyEvent) any(),
mFilter2SequenceCaptor.capture().intValue()))
.thenReturn(true);
}
@@ -122,7 +122,7 @@ public class KeyEventDispatcherTest {
@Test
public void testNotifyKeyEvent_boundServiceDoesntProcessEvents_shouldReturnFalse() {
KeyEventFilter keyEventFilter = mock(KeyEventFilter.class);
- when(keyEventFilter.onKeyEvent((KeyEvent) anyObject(), anyInt())).thenReturn(false);
+ when(keyEventFilter.onKeyEvent((KeyEvent) any(), anyInt())).thenReturn(false);
assertFalse(mKeyEventDispatcher
.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(keyEventFilter)));
assertFalse(isTimeoutPending(mMessageCapturingHandler));
@@ -159,7 +159,7 @@ public class KeyEventDispatcherTest {
mFilter1SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -189,7 +189,7 @@ public class KeyEventDispatcherTest {
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -261,7 +261,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -278,7 +278,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -293,7 +293,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -327,7 +327,7 @@ public class KeyEventDispatcherTest {
mFilter1SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -344,7 +344,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index c62cae5e9b6e..e3515125397a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -21,8 +21,8 @@ import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -94,7 +94,7 @@ public class KeyboardInterceptorTest {
when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mInterceptor.onKeyEvent(event, 0);
- verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+ verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
assertFalse(mHandler.hasMessages());
}
@@ -106,7 +106,7 @@ public class KeyboardInterceptorTest {
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
- verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+ verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
@@ -123,13 +123,13 @@ public class KeyboardInterceptorTest {
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
- verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+ verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mHandler.sendAllMessages();
- verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+ verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index c75cfe67ab79..367f2d143acc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -29,15 +29,14 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.accessibilityservice.GestureDescription.GestureStep;
@@ -223,7 +222,7 @@ public class MotionEventInjectorTest {
verifyNoMoreInteractions(next);
reset(next);
- verifyZeroInteractions(mServiceInterface);
+ verifyNoMoreInteractions(mServiceInterface);
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 2be43c6f21a5..99c922ca30c4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -74,12 +75,13 @@ public class AutoclickControllerTest {
private static class MotionEventCaptor extends BaseEventStreamTransformation {
public MotionEvent downEvent;
-
+ public int eventCount = 0;
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downEvent = event;
+ eventCount++;
break;
}
}
@@ -813,6 +815,57 @@ public class AutoclickControllerTest {
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_clickType_scroll_showsScrollPanelOnlyOnce() {
+ MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+ mController.setNext(motionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+
+ // Set click type to scroll.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL);
+
+ // Mock the scroll panel to verify interactions.
+ AutoclickScrollPanel mockScrollPanel = mock(AutoclickScrollPanel.class);
+ mController.mAutoclickScrollPanel = mockScrollPanel;
+
+ // First hover move event.
+ MotionEvent hoverMove1 = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove1.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove1, hoverMove1, /* policyFlags= */ 0);
+ mTestableLooper.processAllMessages();
+
+ // Verify scroll panel is shown once.
+ verify(mockScrollPanel, times(1)).show();
+ assertThat(motionEventCaptor.downEvent).isNull();
+
+ // Second significant hover move event to trigger another autoclick.
+ MotionEvent hoverMove2 = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 200,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 100f,
+ /* y= */ 100f,
+ /* metaState= */ 0);
+ hoverMove2.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove2, hoverMove2, /* policyFlags= */ 0);
+ mTestableLooper.processAllMessages();
+
+ // Verify scroll panel is still only shown once (not called again).
+ verify(mockScrollPanel, times(1)).show();
+ assertThat(motionEventCaptor.downEvent).isNull();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void hoverOnAutoclickPanel_rightClickType_forceTriggerLeftClick() {
MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
mController.setNext(motionEventCaptor);
@@ -870,6 +923,41 @@ public class AutoclickControllerTest {
mController.onKeyEvent(keyEvent, /* policyFlags= */ 0);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_clickType_doubleclick_triggerClickTwice() {
+ MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+ mController.setNext(motionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+
+ // Set click type to double click.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK);
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+ mTestableLooper.processAllMessages();
+
+ // Verify left click sent.
+ assertThat(motionEventCaptor.downEvent).isNotNull();
+ assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(motionEventCaptor.eventCount).isEqualTo(2);
+ }
+
private MotionEvent getFakeMotionHoverMoveEvent() {
return MotionEvent.obtain(
/* downTime= */ 0,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 4ea5fcfd79c8..31cdd6c3a658 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -43,8 +43,8 @@ import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.gt;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 0c92abce7254..9a241043c52f 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -19,11 +19,11 @@ package com.android.server.accounts;
import static android.database.sqlite.SQLiteDatabase.deleteDatabase;
import static org.mockito.ArgumentMatchers.contains;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 61ac74cc3490..514c07827ae9 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 1627f683cd3e..6d656609c759 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -58,10 +58,10 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3475c8f5444d..20a95e90b668 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
@@ -196,7 +195,7 @@ public class AttentionManagerServiceTest {
@Test
public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
- verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
+ verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
index d5638e9346f6..d89cf7bb5db5 100644
--- a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
@@ -16,7 +16,7 @@
package com.android.server.audio;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -107,13 +107,13 @@ public class MusicFxHelperTest {
List<ResolveInfo> list, int bind, int broadcast, String packageName, int audioSession,
int uid) {
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
- doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+ doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(any(), anyInt());
if (list != null && list.size() != 0) {
try {
doReturn(uid).when(mMockPackageManager)
- .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+ .getPackageUidAsUser(eq(packageName), any(), anyInt());
doReturn(mMusicFxUid).when(mMockPackageManager)
- .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+ .getPackageUidAsUser(eq(mMusicFxPkgName), any(), anyInt());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "NameNotFoundException: " + e);
}
@@ -123,8 +123,8 @@ public class MusicFxHelperTest {
packageName, audioSession);
mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
verify(mMockContext, times(bind))
- .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
- verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+ .bindServiceAsUser(any(), any(), anyInt(), any());
+ verify(mMockContext, times(broadcast)).sendBroadcastAsUser(any(), any());
}
/**
@@ -136,13 +136,13 @@ public class MusicFxHelperTest {
List<ResolveInfo> list, int unBind, int broadcast, String packageName,
int audioSession, int uid) {
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
- doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+ doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(any(), anyInt());
if (list != null && list.size() != 0) {
try {
doReturn(uid).when(mMockPackageManager)
- .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+ .getPackageUidAsUser(eq(packageName), any(), anyInt());
doReturn(mMusicFxUid).when(mMockPackageManager)
- .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+ .getPackageUidAsUser(eq(mMusicFxPkgName), any(), anyInt());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "NameNotFoundException: " + e);
}
@@ -151,8 +151,8 @@ public class MusicFxHelperTest {
Intent intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION,
packageName, audioSession);
mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
- verify(mMockContext, times(unBind)).unbindService(anyObject());
- verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+ verify(mMockContext, times(unBind)).unbindService(any());
+ verify(mMockContext, times(broadcast)).sendBroadcastAsUser(any(), any());
}
/**
@@ -160,8 +160,8 @@ public class MusicFxHelperTest {
*/
private void sendMessage(int msgId, int uid, int unBinds, int broadcasts) {
mMusicFxHelper.handleMessage(Message.obtain(null, msgId, uid /* arg1 */, 0 /* arg2 */));
- verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(anyObject(), anyObject());
- verify(mMockContext, times(unBinds)).unbindService(anyObject());
+ verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(any(), any());
+ verify(mMockContext, times(unBinds)).unbindService(any());
}
/**
@@ -209,15 +209,15 @@ public class MusicFxHelperTest {
intent.setPackage(mTestPkg1);
mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
verify(mMockContext, times(0))
- .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
- verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+ .bindServiceAsUser(any(), any(), anyInt(), any());
+ verify(mMockContext, times(0)).sendBroadcastAsUser(any(), any());
intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
intent.setPackage(mTestPkg2);
mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
verify(mMockContext, times(0))
- .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
- verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+ .bindServiceAsUser(any(), any(), anyInt(), any());
+ verify(mMockContext, times(0)).sendBroadcastAsUser(any(), any());
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index d8a616214f96..a5d6a19d1491 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -42,7 +42,6 @@ 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.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -451,7 +450,7 @@ public class AuthSessionTest {
assertEquals(startFingerprintNow ? BiometricSensor.STATE_AUTHENTICATING
: BiometricSensor.STATE_COOKIE_RETURNED,
session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
- verify(mBiometricContext).updateContext((OperationContextExt) anyObject(),
+ verify(mBiometricContext).updateContext((OperationContextExt) any(),
eq(session.isCrypto()));
// start fingerprint sensor if it was delayed
@@ -554,7 +553,7 @@ public class AuthSessionTest {
session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
verify(mBiometricFrameworkStatsLogger, times(1)).authenticate(
- (OperationContextExt) anyObject(),
+ (OperationContextExt) any(),
eq(BiometricsProtoEnums.MODALITY_FACE),
eq(BiometricsProtoEnums.ACTION_UNKNOWN),
eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
@@ -582,10 +581,10 @@ public class AuthSessionTest {
session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null);
verify(mBiometricFrameworkStatsLogger, never()).authenticate(
- anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+ any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
anyBoolean(), anyInt(), eq(-1f));
verify(mBiometricFrameworkStatsLogger, never()).error(
- anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+ any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
anyInt(), anyInt());
}
@@ -605,7 +604,7 @@ public class AuthSessionTest {
session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null);
verify(mBiometricFrameworkStatsLogger, times(1)).error(
- (OperationContextExt) anyObject(),
+ (OperationContextExt) any(),
eq(BiometricsProtoEnums.MODALITY_FACE),
eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
@@ -632,7 +631,7 @@ public class AuthSessionTest {
session.onDialogDismissed(DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, null);
verify(mBiometricFrameworkStatsLogger, times(1)).error(
- (OperationContextExt) anyObject(),
+ (OperationContextExt) any(),
eq(BiometricsProtoEnums.MODALITY_FACE),
eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 7a770338a34b..f4e87177e072 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -172,7 +172,7 @@ public class FaceDetectClientTest {
client.onInteractionDetected();
client.stopHalOperation();
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
private FaceDetectClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 67fc564fa778..2e07cd8ae698 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -22,18 +22,25 @@ import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -49,6 +56,7 @@ import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,6 +73,9 @@ public class SensorControllerTest {
private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
+ private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f};
+ private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f};
+
@Mock
private SensorManagerInternal mSensorManagerInternalMock;
@Mock
@@ -155,6 +166,53 @@ public class SensorControllerTest {
}
@Test
+ public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> sensorController.sendSensorAdditionalInfo(
+ new Binder("invalidSensorToken"), info));
+ }
+
+ @Test
+ public void sendSensorAdditionalInfo_success() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ clearInvocations(mSensorManagerInternalMock);
+ when(mSensorManagerInternalMock.sendSensorAdditionalInfo(
+ anyInt(), anyInt(), anyInt(), anyLong(), any()))
+ .thenReturn(true);
+ IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet());
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ sensorController.sendSensorAdditionalInfo(token, info);
+
+ InOrder inOrder = inOrder(mSensorManagerInternalMock);
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ }
+
+ @Test
public void close_unregistersSensors() throws Exception {
SensorController sensorController = doCreateSensorSuccessfully();
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index b445226be60f..4fa75b9823e0 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -23,7 +23,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -132,8 +132,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -147,7 +147,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -157,8 +157,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -172,7 +172,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -187,7 +187,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -203,7 +203,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -216,8 +216,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -230,8 +230,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -248,7 +248,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -265,7 +265,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -513,7 +513,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -528,7 +528,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index 195ab68427b9..9d37b99c5bf4 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.Handler;
@@ -98,10 +98,10 @@ public class ContentProtectionAllowlistManagerTest {
@Test
public void constructor() {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -110,10 +110,10 @@ public class ContentProtectionAllowlistManagerTest {
mTestLooper.dispatchAll();
assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -126,8 +126,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -142,8 +142,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -153,11 +153,11 @@ public class ContentProtectionAllowlistManagerTest {
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -169,11 +169,11 @@ public class ContentProtectionAllowlistManagerTest {
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -188,8 +188,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -205,8 +205,8 @@ public class ContentProtectionAllowlistManagerTest {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -223,8 +223,8 @@ public class ContentProtectionAllowlistManagerTest {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -232,10 +232,10 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -248,9 +248,9 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -263,9 +263,9 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -276,8 +276,8 @@ public class ContentProtectionAllowlistManagerTest {
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -291,7 +291,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -309,7 +309,7 @@ public class ContentProtectionAllowlistManagerTest {
// Does not rethrow
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -321,8 +321,8 @@ public class ContentProtectionAllowlistManagerTest {
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -338,7 +338,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -355,7 +355,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService, times(2))
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index b012aaaed3bf..cd36a1889d2f 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -27,7 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyCache;
@@ -112,8 +112,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -125,8 +125,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -138,8 +138,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -152,7 +152,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -166,7 +166,7 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -179,7 +179,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -192,7 +192,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -289,8 +289,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -302,8 +302,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -316,7 +316,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -339,7 +339,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -362,7 +362,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -385,7 +385,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -408,7 +408,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
private void putGlobalSettings(String key, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
index 6a7e2865fb32..563a6799e9e5 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
@@ -20,7 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -87,7 +87,7 @@ public class RemoteContentProtectionServiceTest {
@Test
public void doesNotAutoConnect() {
assertThat(mConnectCallCount).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
}
@Test
@@ -124,7 +124,7 @@ public class RemoteContentProtectionServiceTest {
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ true);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
@@ -133,7 +133,7 @@ public class RemoteContentProtectionServiceTest {
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ false);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 0f3f27aa2896..9e98af32d6e5 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -207,7 +207,7 @@ public class ProviderRegistryGetSessionTest {
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
"unsupportedKey", providerPendingIntentResponse);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
@@ -216,7 +216,7 @@ public class ProviderRegistryGetSessionTest {
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY, null);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 5582e13cbb4d..d5fe0bf9cfdf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -23,9 +23,9 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 01bcc2584fe1..c50c62323212 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -88,7 +88,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.testng.Assert.assertThrows;
@@ -5305,7 +5304,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// both the user restriction and the policy were set by the PO.
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
MANAGED_PROFILE_USER_ID);
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5339,7 +5338,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// not wiped.
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5380,7 +5379,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
// DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
}
@@ -7535,7 +7534,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, false);
@@ -7548,7 +7547,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
// Pretend the alarm went off.
@@ -7561,7 +7560,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
// Apps shouldn't be suspended yet.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
@@ -7570,7 +7569,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was not set.
- verifyZeroInteractions(getServices().alarmManager);
+ verifyNoMoreInteractions(getServices().alarmManager);
// Now the user should see a notification about suspended apps.
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
@@ -8754,7 +8753,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
// Verify that EuiccManager was not called to delete the subscription.
- verifyZeroInteractions(getServices().euiccManager);
+ verifyNoMoreInteractions(getServices().euiccManager);
}
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 03aaeb7e0db8..b52eb0a6857a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index 0a696ef44897..b01895a527c6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -27,7 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index e20f1e7065d4..a39f07105eab 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -31,7 +31,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -226,7 +226,7 @@ public class SystemAppUpdateTrackerTest {
assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
// getApplicationLocales should be never be invoked if not a system app.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if not a system app.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to the file if not a system app.
@@ -244,7 +244,7 @@ public class SystemAppUpdateTrackerTest {
Binder.getCallingUid());
// getApplicationLocales should be never be invoked if not installer is not present.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if installer is not present.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to file if no installer present.
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 565a9b6c1c44..4d2dcf65bfeb 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -80,6 +80,10 @@ public class ContextHubEndpointTest {
new HubMessage.Builder(SAMPLE_MESSAGE_TYPE, new byte[] {1, 2, 3, 4, 5})
.setResponseRequired(true)
.build();
+ private static final HubMessage SAMPLE_UNRELIABLE_MESSAGE =
+ new HubMessage.Builder(SAMPLE_MESSAGE_TYPE, new byte[] {1, 2, 3, 4, 5})
+ .setResponseRequired(false)
+ .build();
private ContextHubClientManager mClientManager;
private ContextHubEndpointManager mEndpointManager;
@@ -260,6 +264,24 @@ public class ContextHubEndpointTest {
unregisterExampleEndpoint(endpoint);
}
+ @Test
+ public void testUnreliableMessage() throws RemoteException {
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+ int sessionId = openTestSession(endpoint);
+
+ mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE);
+ ArgumentCaptor<HubMessage> messageCaptor = ArgumentCaptor.forClass(HubMessage.class);
+ verify(mMockCallback).onMessageReceived(eq(sessionId), messageCaptor.capture());
+ assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE);
+
+ // Confirm we can send another message
+ mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE);
+ verify(mMockCallback, times(2)).onMessageReceived(eq(sessionId), messageCaptor.capture());
+ assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
/** A helper method to create a session and validates reliable message sending. */
private void testMessageTransactionInternal(
IContextHubEndpoint endpoint, boolean deliverMessageStatus) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index a58a9cd2a28f..4a05ea68daec 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -48,7 +48,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -488,7 +488,7 @@ public class MediaProjectionManagerServiceTest {
projection.stop(StopReason.STOP_UNKNOWN);
- verifyZeroInteractions(mMediaProjectionMetricsLogger);
+ verifyNoMoreInteractions(mMediaProjectionMetricsLogger);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 570256bf43e6..a46fc2206cf5 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -35,7 +35,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index da14e451d656..03ba3b655fa8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -25,11 +25,11 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index bbc2cb23b269..242ebc550d16 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -59,10 +59,10 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.waitOnMainThread;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index f5690b77d2fe..46c532bc40fa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -23,9 +23,9 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.parceled;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 19c26f0b60ab..88395a4889c4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -21,10 +21,10 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Matchers.notNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
index ee1bf38a6b24..7ea33ad1184b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
@@ -18,8 +18,8 @@ package com.android.server.pm;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index d55f96782084..2ed27048c288 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
@@ -273,7 +273,7 @@ public class DynamicCodeLoggerTests {
assertThat(mMessagesForUid).isEmpty();
assertThat(mWriteTriggered).isFalse();
- verifyZeroInteractions(mPM);
+ verifyNoMoreInteractions(mPM);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
index 3551af83aa47..5e5be12665d6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
@@ -17,10 +17,10 @@
package com.android.server.pm.permission;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 2a4c3fdf8f43..64f88192a0c5 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -21,8 +21,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
index 3cdf1098622a..1b93d4a36ec7 100644
--- a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
@@ -48,10 +48,10 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index b647b99df894..4a97b4670289 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -18,11 +18,11 @@ package com.android.server.storage;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 9a7abd43aab6..15a60dc600e5 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -62,9 +62,9 @@ import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.intThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
index 4eb2474da740..770dfe3666b0 100644
--- a/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
@@ -20,8 +20,8 @@ import static com.android.server.utils.PriorityDump.dump;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.same;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index bf99b6af2345..3dcd1d7b7da5 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -43,7 +43,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
-import org.mockito.Matchers;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import java.util.concurrent.CountDownLatch;
@@ -340,7 +340,7 @@ public class WebViewUpdateServiceTest {
runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
- Matchers.anyObject());
+ ArgumentMatchers.any());
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
@@ -378,7 +378,7 @@ public class WebViewUpdateServiceTest {
runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
- Matchers.anyObject());
+ ArgumentMatchers.any());
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
@@ -822,7 +822,7 @@ public class WebViewUpdateServiceTest {
checkPreparationPhasesForPackage(firstPackage, 1);
Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
- Mockito.anyObject());
+ Mockito.any());
}
@Test
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 4c0361d30d67..cdc28a13fc8e 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -25,10 +25,10 @@ import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyList;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
diff --git a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
index c8afb78bc12f..630a7e47fa48 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
@@ -126,7 +126,7 @@ public final class GnssTimeUpdateServiceTest {
locationListener.onLocationChanged(location);
verify(mMockLocationManager).removeUpdates(locationListener);
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
verify(mMockAlarmManager).set(
eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
anyLong(),
@@ -150,7 +150,7 @@ public final class GnssTimeUpdateServiceTest {
// Verify the service returned to location listening.
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
}
// Tests what happens when a call is made to startGnssListeningInternal() when service is
@@ -172,7 +172,7 @@ public final class GnssTimeUpdateServiceTest {
// listening again.
verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue());
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
}
private void advanceServiceToSleepingState(
@@ -190,7 +190,7 @@ public final class GnssTimeUpdateServiceTest {
any(), any(), any(), locationListenerCaptor.capture());
LocationListener locationListener = locationListenerCaptor.getValue();
Location location = new Location(LocationManager.GPS_PROVIDER);
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
locationListener.onLocationChanged(location);
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 66d7611a29c6..d34d74d80882 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -11,13 +11,8 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-android_test {
- name: "FrameworksUiServicesTests",
-
- // Include test java files
- srcs: [
- "src/**/*.java",
- ],
+java_defaults {
+ name: "FrameworksUiServicesTests-defaults",
static_libs: [
"compatibility-device-util-axt-minus-dexmaker",
@@ -95,12 +90,72 @@ android_test {
javacflags: ["-parameters"],
}
-test_module_config {
- name: "FrameworksUiServicesTests_notification",
- base: "FrameworksUiServicesTests",
- test_suites: [
- "automotive-tests",
- "device-tests",
+// Utility files used by multiple tests
+filegroup {
+ name: "shared-srcs",
+ srcs: [
+ "src/android/app/ExampleActivity.java",
+ "src/android/app/NotificationSystemUtil.java",
+ "src/com/android/frameworks/tests/uiservices/DummyProvider.java",
+ "src/com/android/internal/logging/InstanceIdSequenceFake.java",
+ "src/com/android/server/UiServiceTestCase.java",
+ "src/com/android/server/notification/ZenChangeOrigin.java",
+ "src/com/android/server/notification/ZenModeEventLoggerFake.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-srcs",
+ srcs: [
+ "src/**/Notification*.java",
+ "src/com/android/server/notification/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-zen-srcs",
+ srcs: [
+ "src/android/app/NotificationManagerZenTest.java",
+ "src/com/android/server/notification/Zen*Test.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files but not the notification & zen ones which are separated
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ exclude_srcs: [
+ ":notification-srcs",
+ ":notification-zen-srcs",
+ ],
+
+ defaults: ["FrameworksUiServicesTests-defaults"],
+}
+
+android_test {
+ name: "FrameworksUiServicesNotificationTests",
+ srcs: [
+ ":notification-srcs",
+ ":shared-srcs",
+ ],
+ exclude_srcs: [":notification-zen-srcs"],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-tests.xml",
+}
+
+android_test {
+ name: "FrameworksUiServicesZenTests",
+ srcs: [
+ ":notification-zen-srcs",
+ ":shared-srcs",
],
- exclude_annotations: ["androidx.test.filters.LargeTest"],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-zen-tests.xml",
}
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index 11e8f090a8fa..93c8c72630f1 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Runs Frameworks UI Services Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksUiServicesTests.apk" />
</target_preparer>
diff --git a/services/tests/uiservicestests/notification-tests.xml b/services/tests/uiservicestests/notification-tests.xml
new file mode 100644
index 000000000000..acfd844efe26
--- /dev/null
+++ b/services/tests/uiservicestests/notification-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (notifications subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesNotificationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesNotificationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/notification-zen-tests.xml b/services/tests/uiservicestests/notification-zen-tests.xml
new file mode 100644
index 000000000000..01d8aab83d59
--- /dev/null
+++ b/services/tests/uiservicestests/notification-zen-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (zen mode subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesZenTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesZenTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 9930c9f07ed8..7b1ce446da7c 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -62,7 +62,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -992,7 +991,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
() -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
@@ -1008,7 +1007,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
() -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index 6b989cb0aaee..b33233107766 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -38,7 +38,6 @@ import android.os.IInterface;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Condition;
-import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -47,8 +46,6 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
public class ConditionProvidersTest extends UiServiceTestCase {
private ConditionProviders mProviders;
@@ -172,15 +169,4 @@ public class ConditionProvidersTest extends UiServiceTestCase {
assertTrue(mProviders.getApproved(userId, true).isEmpty());
}
-
- @Test
- public void getDefaultDndAccessPackages_returnsPackages() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages,
- "com.example.a:com.example.b::::com.example.c");
-
- List<String> packages = ConditionProviders.getDefaultDndAccessPackages(mContext);
-
- assertThat(packages).containsExactly("com.example.a", "com.example.b", "com.example.c");
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5ce9a3e8d4d4..8023bdd08927 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
@@ -184,7 +183,7 @@ public class DefaultDeviceEffectsApplierTest {
mApplier.apply(noEffects, ORIGIN_USER_IN_SYSTEMUI);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
- verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
+ verifyNoMoreInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
}
@Test
@@ -252,8 +251,8 @@ public class DefaultDeviceEffectsApplierTest {
// Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
verify(mColorDisplayManager).setSaturationLevel(eq(0));
- verifyZeroInteractions(mPowerManager);
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mPowerManager);
+ verifyNoMoreInteractions(mUiModeManager);
}
@Test
@@ -269,7 +268,7 @@ public class DefaultDeviceEffectsApplierTest {
ORIGIN_APP);
// Effect was not yet applied, but a broadcast receiver was registered.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture(), anyInt());
assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
@@ -337,7 +336,7 @@ public class DefaultDeviceEffectsApplierTest {
origin.value());
// Effect was not applied, will be on next screen-off.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(any(),
argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index a9759c8a61f3..11143653a75d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -50,9 +50,9 @@ import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -60,7 +60,7 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
@@ -313,7 +313,7 @@ public class GroupHelperTest extends UiServiceTestCase {
getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -327,7 +327,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -340,7 +340,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -353,7 +353,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1744,7 +1744,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1759,7 +1759,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1775,7 +1775,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1791,7 +1791,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1811,7 +1811,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1830,7 +1830,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1853,7 +1853,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
notificationList.add(child);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1877,7 +1877,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(child);
summaryByGroup.put(summary.getGroupKey(), summary);
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -2209,7 +2209,7 @@ public class GroupHelperTest extends UiServiceTestCase {
childrenToRemove.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2273,7 +2273,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove some child notifications from the valid group, transform into a singleton group
Mockito.reset(mCallback);
@@ -2329,7 +2329,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2343,7 +2343,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
summaryByGroup);
// Check that nothing was force grouped
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -3837,7 +3837,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@@ -3861,7 +3861,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
// FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -4498,7 +4498,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
// no autogrouping should have occurred
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 98440ecdad82..9c85b04fc4ff 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -40,10 +40,10 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 076e3e9fcc24..aef3d30dba93 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -36,8 +36,8 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index bc01fc4f29c0..8fbd3bda98dd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -48,7 +48,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -1114,8 +1113,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
verifyDelayedVibrate(
mAttentionHelper.getVibratorHelper().createFallbackVibration(
/* insistent= */ false));
- verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(),
- anyObject(), anyFloat());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(),
+ any(), anyFloat());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index 41011928f8b3..8de2b9c6d3a6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -32,9 +32,9 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 5aecac2cab78..9e8023ff344f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -23,9 +23,9 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index dd5c601619a0..bc8b7becc919 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -168,10 +168,10 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index a02f628ce9b7..43228f434997 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -96,7 +96,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -6372,7 +6372,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel same = new NotificationChannel("id", "Bah", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_P, 0, same, true, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6398,7 +6398,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.updateNotificationChannel(PKG_P, 0, same, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6412,7 +6412,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void setShowBadge_same_doesNotRequestSort() {
mHelper.setShowBadge(PKG_P, 0, true); // true == DEFAULT_SHOW_BADGE
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index ec428d506e7b..91e4fd623cd4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -25,8 +25,8 @@ import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 31436c602e56..62f7471597f0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -25,7 +25,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index dcd56e07f0d2..7f05ddefa891 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -28,9 +28,9 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
deleted file mode 100644
index 154a905c776b..000000000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ZenConfigTrimmerTest extends UiServiceTestCase {
-
- private static final String TRUSTED_PACKAGE = "com.trust.me";
- private static final int ONE_PERCENT = 1_500;
-
- private ZenConfigTrimmer mTrimmer;
-
- @Before
- public void setUp() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages, TRUSTED_PACKAGE);
-
- mTrimmer = new ZenConfigTrimmer(mContext);
- }
-
- @Test
- public void trimToMaximumSize_belowMax_untouched() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "4", "5");
- }
-
- @Test
- public void trimToMaximumSize_exceedsMax_removesAllRulesOfLargestPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
- addZenRule(config, "7", "pkg4", 38 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "6");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly("pkg1", "pkg3");
- }
-
- @Test
- public void trimToMaximumSize_keepsRulesFromTrustedPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", TRUSTED_PACKAGE, 60 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("4", "5");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly(TRUSTED_PACKAGE, "pkg2");
- }
-
- /**
- * Create a ZenRule that, when serialized to a Parcel, will take <em>approximately</em>
- * {@code desiredSize} bytes (within 100 bytes). Try to make the tests not rely on a very tight
- * fit.
- */
- private static void addZenRule(ZenModeConfig config, String id, String pkg, int desiredSize) {
- ZenRule rule = new ZenRule();
- rule.id = id;
- rule.pkg = pkg;
- config.automaticRules.put(id, rule);
-
- // Make the ZenRule as large as desired. Not to the exact byte, because otherwise this
- // test would have to be adjusted whenever we change the parceling of ZenRule in any way.
- // (Still might need adjustment if we change the serialization _significantly_).
- int nameLength = desiredSize - id.length() - pkg.length() - 232;
- rule.name = "A".repeat(nameLength);
-
- Parcel verification = Parcel.obtain();
- try {
- verification.writeParcelable(rule, 0);
- assertThat(verification.dataSize()).isWithin(100).of(desiredSize);
- } finally {
- verification.recycle();
- }
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 5377102e5a13..bfce647356ec 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -90,7 +90,6 @@ import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.Flags.FLAG_LIMIT_ZEN_CONFIG_SIZE;
import static com.android.server.notification.Flags.FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING;
import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -238,7 +237,6 @@ import java.util.stream.Collectors;
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
-@EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE) // Should be parameterization, but off path does nothing.
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
@@ -7485,45 +7483,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(getZenRule(ruleId).lastActivation).isNull();
}
- @Test
- @EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE)
- public void addAutomaticZenRule_trimsConfiguration() {
- mZenModeHelper.mConfig.automaticRules.clear();
- AutomaticZenRule smallRule = new AutomaticZenRule.Builder("Reasonable", CONDITION_ID)
- .setConfigurationActivity(new ComponentName(mPkg, "cls"))
- .build();
- AutomaticZenRule systemRule = new AutomaticZenRule.Builder("System", CONDITION_ID)
- .setOwner(new ComponentName("android", "ScheduleConditionProvider"))
- .build();
-
- AutomaticZenRule bigRule = new AutomaticZenRule.Builder("Yuge", CONDITION_ID)
- .setConfigurationActivity(new ComponentName("evil.package", "cls"))
- .setTriggerDescription("0123456789".repeat(6000)) // ~60k bytes utf16.
- .build();
-
- String systemRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android",
- systemRule, ORIGIN_SYSTEM, "add", SYSTEM_UID);
- String smallRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, smallRule,
- ORIGIN_APP, "add", CUSTOM_PKG_UID);
- String bigRuleId1 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1);
-
- String bigRuleId2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1, bigRuleId2);
-
- // This should go over the threshold
- String bigRuleId3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-
- // Rules from evil.package are gone.
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId);
- }
-
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 79e272b7ec01..01698b5bdd6b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -33,7 +33,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -133,7 +132,7 @@ public class VibratorControlServiceTest {
mVibratorControlService.registerVibratorController(controller1);
mVibratorControlService.unregisterVibratorController(controller2);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
assertThat(controller1.isLinkedToDeath).isTrue();
}
@@ -187,7 +186,7 @@ public class VibratorControlServiceTest {
verify(mStatsLoggerMock).logVibrationParamResponseIgnored();
verifyNoMoreInteractions(mStatsLoggerMock);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -242,7 +241,7 @@ public class VibratorControlServiceTest {
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -280,7 +279,7 @@ public class VibratorControlServiceTest {
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
index 9a59ede20e23..011971d85f42 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.media.soundtrigger.RecognitionStatus;
import android.media.soundtrigger_middleware.RecognitionEventSys;
@@ -76,7 +75,7 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest {
assertEquals(event.halEventReceivedMillis, -1);
assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED);
assertFalse(event.recognitionEvent.recognitionStillActive);
- verifyZeroInteractions(mGlobalCallback);
+ verifyNoMoreInteractions(mGlobalCallback);
clearInvocations(callback, mUnderlying);
mNotifier.setActive(false);
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index d961a6acc9c1..50fc77676958 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -21,7 +21,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static org.junit.Assert.assertEquals;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
@@ -90,7 +90,7 @@ public class ModifierShortcutManagerTests {
XmlResourceParser testBookmarks = mResources.getXml(
com.android.frameworks.wmtests.R.xml.bookmarks);
- doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
@@ -106,7 +106,7 @@ public class ModifierShortcutManagerTests {
doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
- doReturn(testResolveInfo).when(mPackageManager).resolveActivity(anyObject(), anyInt());
+ doReturn(testResolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
.getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
anyInt());
@@ -140,10 +140,10 @@ public class ModifierShortcutManagerTests {
public void test_shortcutInfoFromIntent_appIntent() {
Intent mockIntent = mock(Intent.class);
ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
- when(mockActivityInfo.loadLabel(anyObject())).thenReturn("label");
+ when(mockActivityInfo.loadLabel(any())).thenReturn("label");
mockActivityInfo.packageName = "android";
when(mockActivityInfo.getIconResource()).thenReturn(R.drawable.sym_def_app_icon);
- when(mockIntent.resolveActivityInfo(anyObject(), anyInt())).thenReturn(mockActivityInfo);
+ when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);
KeyboardShortcutInfo info = mModifierShortcutManager.shortcutInfoFromIntent(
'a', mockIntent, true);
@@ -161,7 +161,7 @@ public class ModifierShortcutManagerTests {
Intent mockSelector = mock(Intent.class);
ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
mockActivityInfo.name = com.android.internal.app.ResolverActivity.class.getName();
- when(mockIntent.resolveActivityInfo(anyObject(), anyInt())).thenReturn(mockActivityInfo);
+ when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);
when(mockIntent.getSelector()).thenReturn(mockSelector);
when(mockSelector.getCategories()).thenReturn(
Collections.singleton(Intent.CATEGORY_APP_BROWSER));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 8ede9efd32a0..c57adfd69b06 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -44,7 +44,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static java.util.Collections.unmodifiableMap;
@@ -111,7 +111,7 @@ class ShortcutKeyTestBase {
mContext = spy(getInstrumentation().getTargetContext());
mResources = spy(mContext.getResources());
mPackageManager = spy(mContext.getPackageManager());
- doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
doReturn(mResources).when(mContext).getResources();
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8992c2e632b2..7f242dea9f45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2021,8 +2021,6 @@ public class ActivityRecordTests extends WindowTestsBase {
display.setFixedRotationLaunchingAppUnchecked(activity);
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
- assertTrue(displayRotation.isRotatingSeamlessly());
-
// The launching rotated app should not be cleared when waiting for remote rotation.
display.continueUpdateOrientationForDiffOrienLaunchingApp();
assertTrue(display.isFixedRotationLaunchingApp(activity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 86d901b640ff..862ce51d3459 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -75,13 +76,15 @@ import java.util.concurrent.TimeUnit;
* Tests for the {@link ActivityTaskSupervisor} class.
*
* Build/Install/Run:
- * atest WmTests:ActivityTaskSupervisorTests
+ * atest WmTests:ActivityTaskSupervisorTests
*/
@MediumTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityTaskSupervisorTests extends WindowTestsBase {
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
+ private static final int DEFAULT_CALLING_PID = -1;
+ private static final int DEFAULT_CALLING_UID = -1;
/**
* Ensures that an activity is removed from the stopping activities list once it is resumed.
@@ -110,7 +113,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
.setCreateTask(true).build();
final ConditionVariable condition = new ConditionVariable();
final WaitResult taskToFrontWait = new WaitResult();
- final ComponentName[] launchedComponent = { null };
+ final ComponentName[] launchedComponent = {null};
// Create a new thread so the waiting method in test can be notified.
new Thread(() -> {
synchronized (mAtm.mGlobalLock) {
@@ -408,7 +411,8 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
anyInt(), any());
- mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions);
+ mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+ activity.getRootTaskId(), safeOptions);
assertThat(activity.mLaunchCookie).isEqualTo(launchCookie);
verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
@@ -426,12 +430,62 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
anyInt(), any());
- mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions);
+ mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+ activity.getRootTaskId(), safeOptions);
assertThat(activity.mLaunchCookie).isNull();
verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
}
+ /** Verifies that launch from recents doesn't set the launch cookie on the activity. */
+ @Test
+ public void testStartActivityFromRecents_inMultiWindowRootTask_homeNotMoved() {
+ final Task multiWindowRootTask = new TaskBuilder(mSupervisor).setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).setOnTop(true).build();
+
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setParentTask(
+ multiWindowRootTask).setCreateTask(true).build();
+
+ SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+ ActivityOptions.makeBasic().toBundle(),
+ Binder.getCallingPid(), Binder.getCallingUid());
+
+ doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
+ anyInt(), any());
+
+ mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+ activity.getRootTaskId(), safeOptions);
+
+ verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
+ verify(mRootWindowContainer.getDefaultTaskDisplayArea(), never()).moveHomeRootTaskToFront(
+ any());
+ verify(multiWindowRootTask.getDisplayArea(), never()).moveHomeRootTaskToFront(any());
+ }
+
+ /** Verifies that launch from recents doesn't set the launch cookie on the activity. */
+ @Test
+ public void testStartActivityFromRecents_inFullScreenRootTask_homeMovedToFront() {
+ final Task fullscreenRootTask = new TaskBuilder(mSupervisor).setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN).setOnTop(true).build();
+
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setParentTask(
+ fullscreenRootTask).setCreateTask(true).build();
+
+ SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+ ActivityOptions.makeBasic().toBundle(),
+ Binder.getCallingPid(), Binder.getCallingUid());
+
+ doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
+ anyInt(), any());
+
+ mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+ activity.getRootTaskId(), safeOptions);
+
+ verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
+ verify(mRootWindowContainer.getDefaultTaskDisplayArea()).moveHomeRootTaskToFront(any());
+ verify(fullscreenRootTask.getDisplayArea()).moveHomeRootTaskToFront(any());
+ }
+
@Test
public void testOpaque_leafTask_occludingActivity_isOpaque() {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java
new file mode 100644
index 000000000000..bdd57bce7c09
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatSafeRegionPolicy}.
+ * Build/Install/Run:
+ * atest WmTests:AppCompatSafeRegionPolicyTests
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+@EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+public class AppCompatSafeRegionPolicyTests extends WindowTestsBase {
+
+ @Test
+ public void testHasNeedsSafeRegion_returnTrue() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponent();
+ robot.setNeedsSafeRegionBounds(/*needsSafeRegionBounds*/ true);
+
+ robot.checkNeedsSafeRegionBounds(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testDoesNotHaveNeedsSafeRegion_returnFalse() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponent();
+ robot.setNeedsSafeRegionBounds(/*needsSafeRegionBounds*/ false);
+
+ robot.checkNeedsSafeRegionBounds(/* expected */ false);
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<AppCompatSafeRegionPolicyRobotTest> consumer) {
+ final AppCompatSafeRegionPolicyRobotTest robot =
+ new AppCompatSafeRegionPolicyRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class AppCompatSafeRegionPolicyRobotTest extends AppCompatRobotBase {
+
+ AppCompatSafeRegionPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ }
+
+ @Override
+ void onPostActivityCreation(@NonNull ActivityRecord activity) {
+ super.onPostActivityCreation(activity);
+ spyOn(activity.mAppCompatController.getSafeRegionPolicy());
+ }
+
+ AppCompatSafeRegionPolicy getAppCompatSafeRegionPolicy() {
+ return activity().top().mAppCompatController.getSafeRegionPolicy();
+ }
+
+ void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+ doReturn(needsSafeRegionBounds).when(
+ getAppCompatSafeRegionPolicy()).getNeedsSafeRegionBounds();
+ }
+
+ void checkNeedsSafeRegionBounds(boolean expected) {
+ assertEquals(expected, getAppCompatSafeRegionPolicy().getNeedsSafeRegionBounds());
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index bfd533aa8f79..b2cfbbd23b22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -98,6 +98,24 @@ public class AppCompatUtilsTest extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void getLetterboxReasonString_isLetterboxedForSafeRegionOnly() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ });
+ robot.setIsLetterboxedForFixedOrientationAndAspectRatio(
+ /* forFixedOrientationAndAspectRatio */ false);
+ robot.setIsLetterboxedForDisplayCutout(/* displayCutout */ false);
+ robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ false);
+ robot.setIsLetterboxedForSafeRegionOnlyAllowed(/* safeRegionOnly */ true);
+
+ robot.checkTopActivityLetterboxReason(/* expected */ "SAFE_REGION");
+ });
+ }
+
+ @Test
public void getLetterboxReasonString_aspectRatio() {
runTestScenario((robot) -> {
robot.applyOnActivity((a) -> {
@@ -124,6 +142,7 @@ public class AppCompatUtilsTest extends WindowTestsBase {
/* forFixedOrientationAndAspectRatio */ false);
robot.setIsLetterboxedForDisplayCutout(/* displayCutout */ false);
robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ false);
+ robot.setIsLetterboxedForSafeRegionOnlyAllowed(/* safeRegionOnly */ false);
robot.checkTopActivityLetterboxReason(/* expected */ "UNKNOWN_REASON");
});
@@ -253,6 +272,7 @@ public class AppCompatUtilsTest extends WindowTestsBase {
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
spyOn(activity.mAppCompatController.getAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getSafeRegionPolicy());
}
@Override
@@ -286,6 +306,11 @@ public class AppCompatUtilsTest extends WindowTestsBase {
when(mWindowState.isLetterboxedForDisplayCutout()).thenReturn(displayCutout);
}
+ void setIsLetterboxedForSafeRegionOnlyAllowed(boolean safeRegionOnly) {
+ when(activity().top().mAppCompatController.getSafeRegionPolicy()
+ .isLetterboxedForSafeRegionOnlyAllowed()).thenReturn(safeRegionOnly);
+ }
+
void setFreeformCameraCompatMode(@FreeformCameraCompatMode int mode) {
doReturn(mode).when(activity().top().mDisplayContent.mAppCompatCameraPolicy
.mCameraCompatFreeformPolicy).getCameraCompatMode(activity().top());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 76b994d013f3..ad76662c6c18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -31,6 +31,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
import static com.google.common.truth.Truth.assertThat;
@@ -51,6 +52,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.media.projection.StopReason;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -558,6 +560,22 @@ public class ContentRecorderTests extends WindowTestsBase {
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
}
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testStartRecording_shouldShowSystemDecorations_recordingNotStarted() {
+ defaultInit();
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+
+ spyOn(mVirtualDisplayContent.mDisplay);
+ doReturn(true).when(mVirtualDisplayContent.mDisplay).canHostTasks();
+
+ // WHEN a recording tries to start.
+ mContentRecorder.updateRecording();
+
+ // THEN recording does not start.
+ assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+ }
+
@Test
public void testOnVisibleRequestedChanged_notifiesCallback() {
defaultInit();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index e87e107cd793..00b617e91bfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -41,6 +41,7 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx;
import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_LANDSCAPE_APP_PADDING;
import static com.android.server.wm.DesktopModeBoundsCalculator.centerInScreen;
@@ -382,7 +383,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isUserFullscreenOverrideEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -410,7 +411,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isSystemOverrideToFullscreenEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -893,9 +894,11 @@ public class DesktopModeLaunchParamsModifierTests extends
@Test
@EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
+ Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+ Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
public void testDefaultLandscapeBounds_landscapeDevice_unResizable_landscapeOrientation() {
setupDesktopModeLaunchParamsModifier();
+ final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
LANDSCAPE_DISPLAY_BOUNDS);
@@ -903,11 +906,11 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
-
- final int desiredWidth =
- (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final float displayAspectRatio = (float) LANDSCAPE_DISPLAY_BOUNDS.width()
+ / LANDSCAPE_DISPLAY_BOUNDS.height();
final int desiredHeight =
(int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int desiredWidth = (int) ((desiredHeight - captionHeight) * displayAspectRatio);
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -916,7 +919,8 @@ public class DesktopModeLaunchParamsModifierTests extends
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+ Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
public void testUnResizablePortraitBounds_landscapeDevice_unResizable_portraitOrientation() {
setupDesktopModeLaunchParamsModifier();
@@ -925,6 +929,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final Task task = createTask(display, /* isResizeable */ false);
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
+ final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
@@ -932,7 +937,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final int desiredHeight =
(int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
- final int desiredWidth = (int) (desiredHeight / LETTERBOX_ASPECT_RATIO);
+ final int desiredWidth = (int) ((desiredHeight - captionHeight) / LETTERBOX_ASPECT_RATIO);
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -1070,7 +1075,8 @@ public class DesktopModeLaunchParamsModifierTests extends
@Test
@EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
- Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
+ Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+ Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
public void testDefaultPortraitBounds_portraitDevice_unResizable_portraitOrientation() {
setupDesktopModeLaunchParamsModifier();
@@ -1079,12 +1085,14 @@ public class DesktopModeLaunchParamsModifierTests extends
final Task task = createTask(display, /* isResizeable */ false);
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
+ final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
-
- final int desiredWidth =
- (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final float displayAspectRatio = (float) PORTRAIT_DISPLAY_BOUNDS.height()
+ / PORTRAIT_DISPLAY_BOUNDS.width();
final int desiredHeight =
- (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int desiredWidth =
+ (int) ((desiredHeight - captionHeight) / displayAspectRatio);
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -1093,7 +1101,8 @@ public class DesktopModeLaunchParamsModifierTests extends
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+ Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
public void testUnResizableLandscapeBounds_portraitDevice_unResizable_landscapeOrientation() {
setupDesktopModeLaunchParamsModifier();
@@ -1102,6 +1111,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final Task task = createTask(display, /* isResizeable */ false);
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
+ final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
@@ -1109,7 +1119,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
- final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO);
+ final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO) + captionHeight;
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -1160,6 +1170,32 @@ public class DesktopModeLaunchParamsModifierTests extends
@Test
@EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSizeWithFullscreenOverride_noModifications() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
+ doReturn(true).when(
+ mActivity.mAppCompatController.getAspectRatioOverrides())
+ .hasFullscreenOverride();
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(options.getLaunchBounds(), mResult.mBounds);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
setupDesktopModeLaunchParamsModifier();
@@ -1483,6 +1519,24 @@ public class DesktopModeLaunchParamsModifierTests extends
assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
}
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testFreeformWindowingModeAppliedIfSourceTaskExists() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).build();
+ final Task sourceTask = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+ final ActivityRecord sourceActivity = new ActivityBuilder(task.mAtmService)
+ .setTask(sourceTask).build();
+
+ assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+ .setSource(sourceActivity).calculate());
+ assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
+ }
+
private Task createTask(DisplayContent display, Boolean isResizeable) {
final int resizeMode = isResizeable ? RESIZE_MODE_RESIZEABLE
: RESIZE_MODE_UNRESIZEABLE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index a30591ea7b15..9486bc522a9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,6 +25,7 @@ import static com.android.server.wm.utils.LastCallVerifier.lastCall;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -432,4 +433,32 @@ public class DimmerTests extends WindowTestsBase {
verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
verify(mTransaction).setAlpha(dimLayer, 0.9f);
}
+
+ /**
+ * A window requesting to dim to 0 and without blur would cause the dim to be created and
+ * destroyed continuously.
+ * Ensure the dim layer is not created until the window is requesting valid values.
+ */
+ @Test
+ public void testDimNotCreatedIfNoAlphaNoBlur() {
+ mDimmer.adjustAppearance(mChild1, 0.0f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
+ assertNull(mDimmer.getDimLayer());
+ mDimmer.updateDims(mTransaction);
+ assertNull(mDimmer.getDimLayer());
+
+ mDimmer.adjustAppearance(mChild1, 0.9f, 0);
+ mDimmer.adjustPosition(mChild1, mChild1);
+ assertNotNull(mDimmer.getDimLayer());
+ }
+
+ /**
+ * If there is a blur, then the dim layer is created even though alpha is 0
+ */
+ @Test
+ public void testDimCreatedIfNoAlphaButHasBlur() {
+ mDimmer.adjustAppearance(mChild1, 0.0f, 10);
+ mDimmer.adjustPosition(mChild1, mChild1);
+ assertNotNull(mDimmer.getDimLayer());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0af41ea1f634..89aa3b9a2443 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -56,7 +56,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo;
@@ -174,7 +174,7 @@ public class DisplayAreaTest extends WindowTestsBase {
da1.reduceOnAllTaskDisplayAreas(callback2, 0);
da1.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da2);
+ verifyNoMoreInteractions(da2);
// Traverse the child if the current DA has type ANY
final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
@@ -207,7 +207,7 @@ public class DisplayAreaTest extends WindowTestsBase {
da5.reduceOnAllTaskDisplayAreas(callback2, 0);
da5.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da6);
+ verifyNoMoreInteractions(da6);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index 7033d79d0ee1..9ce4a80616ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -43,7 +43,6 @@ import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
import com.android.server.wm.TransitionController.OnStartCollect;
-import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -218,7 +217,6 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
@Test
public void testWaitForTransition_displaySwitching_waitsForTransitionToBeStarted() {
- mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
assertThat(willWait).isTrue();
@@ -241,7 +239,6 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
@Test
public void testWaitForTransition_displayNotSwitching_doesNotWait() {
- mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ false);
boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
@@ -251,19 +248,7 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
}
@Test
- public void testWaitForTransition_displayIsSwitchingButFlagDisabled_doesNotWait() {
- mSetFlagsRule.disableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
- mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
-
- boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
-
- assertThat(willWait).isFalse();
- verify(mScreenUnblocker, never()).sendToTarget();
- }
-
- @Test
public void testTwoDisplayUpdateAtTheSameTime_bothDisplaysAreUnblocked() {
- mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
prepareSecondaryDisplay();
final WindowState defaultDisplayWindow = newWindowBuilder("DefaultDisplayWindow",
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c81f738138a..ed00a9e8e74b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,7 +30,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.Display.FLAG_TRUSTED;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.Surface.ROTATION_0;
@@ -1706,8 +1709,6 @@ public class DisplayContentTests extends WindowTestsBase {
app.setVisible(true);
doReturn(false).when(app).inTransition();
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
- mStatusBarWindow.finishSeamlessRotation(t);
- mNavBarWindow.finishSeamlessRotation(t);
// The fixed rotation should be cleared and the new rotation is applied to display.
assertFalse(app.hasFixedRotationTransform());
@@ -2925,6 +2926,63 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
}
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_shouldShowSystemDecorationsDisplay() {
+ // Set up a non-default display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_notAllowContentModeSwitchDisplay() {
+ // Set up a non-default display without FLAG_ALLOWS_CONTENT_MODE_SWITCH enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_TRUSTED;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_untrustedDisplay() {
+ // Set up a non-default display without FLAG_TRUSTED enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = (FLAG_ALLOWS_CONTENT_MODE_SWITCH | FLAG_TRUSTED);
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ spyOn(dc.mDisplay);
+ doReturn(false).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+ doReturn(true).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
@EnableFlags(FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
@Test
public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
@@ -2993,23 +3051,6 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(320, displayContent.mBaseDisplayDensity);
}
- @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
- @Test
- public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
- final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
- displayInfo.displayId = DEFAULT_DISPLAY + 1;
- final DisplayContent dc = createNewDisplay(displayInfo);
-
- spyOn(dc.mDisplay);
- doReturn(false).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-
- doReturn(true).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
- }
-
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 81e487a67725..521d8364a31f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -37,7 +37,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index eb6d5cf8bb14..e293c2fbbfb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -19,6 +19,9 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.NO_SURFACE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -244,4 +247,33 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
verify(displayWindowInsetsController, times(1)).setImeInputTargetRequestedVisibility(
eq(true), any());
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ public void testOnPostLayout_resetServerVisibilityWhenImeIsNotDrawn() {
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
+ final WindowState inputTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+ makeWindowVisibleAndDrawn(ime);
+ mImeProvider.setWindowContainer(ime, null, null);
+ mImeProvider.setServerVisible(true);
+ mImeProvider.setClientVisible(true);
+ mImeProvider.updateVisibility();
+ mImeProvider.updateControlForTarget(inputTarget, true /* force */, null /* statsToken */);
+
+ // Calling onPostLayout, as the drawn state is initially false.
+ mImeProvider.onPostLayout();
+ assertTrue(mImeProvider.isSurfaceVisible());
+
+ // Reset window's drawn state
+ ime.mWinAnimator.mDrawState = NO_SURFACE;
+ mImeProvider.onPostLayout();
+ assertFalse(mImeProvider.isServerVisible());
+ assertFalse(mImeProvider.isSurfaceVisible());
+
+ // Set it back to drawn
+ ime.mWinAnimator.mDrawState = HAS_DRAWN;
+ mImeProvider.onPostLayout();
+ assertTrue(mImeProvider.isServerVisible());
+ assertTrue(mImeProvider.isSurfaceVisible());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 67a95de8a5c1..ae005f228969 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -44,6 +44,7 @@ import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.pm.ActivityInfo.WindowLayout;
import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -52,6 +53,7 @@ import androidx.test.filters.MediumTest;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -115,6 +117,7 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
expected.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
expected.mWindowingMode = WINDOWING_MODE_PINNED;
expected.mBounds.set(200, 300, 400, 500);
+ expected.mNeedsSafeRegionBounds = true;
mPersister.putLaunchParams(userId, name, expected);
@@ -187,6 +190,7 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
params.mWindowingMode = WINDOWING_MODE_FREEFORM;
params.mBounds.set(0, 0, 30, 20);
params.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
+ params.mNeedsSafeRegionBounds = true;
final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
params);
@@ -227,6 +231,158 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
}
/**
+ * Tests only needs safe region bounds are not propagated if results are skipped.
+ */
+ @Test
+ public void testSkip_needsSafeRegionBoundsNotModified() {
+ final LaunchParams params1 = new LaunchParams();
+ params1.mNeedsSafeRegionBounds = true;
+ final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
+
+ final LaunchParams params2 = new LaunchParams();
+ params2.mNeedsSafeRegionBounds = false;
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+
+ final LaunchParams result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+ assertEquals(result, positioner2.getLaunchParams());
+ }
+
+ /**
+ * Tests only needs safe region bounds are propagated even if results are continued.
+ */
+ @Test
+ public void testContinue_needsSafeRegionBoundsCarriedOver() {
+ final LaunchParams params1 = new LaunchParams();
+ final InstrumentedPositioner positioner1 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params1);
+
+ final LaunchParams params2 = new LaunchParams();
+ params2.mNeedsSafeRegionBounds = true;
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+
+ final LaunchParams result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+ // Safe region is propagated from positioner1
+ assertEquals(result.mNeedsSafeRegionBounds,
+ positioner2.getLaunchParams().mNeedsSafeRegionBounds);
+ assertEquals(result.mWindowingMode, positioner1.getLaunchParams().mWindowingMode);
+ assertEquals(result.mBounds, positioner1.getLaunchParams().mBounds);
+ assertEquals(result.mPreferredTaskDisplayArea,
+ positioner1.getLaunchParams().mPreferredTaskDisplayArea);
+ }
+
+ /**
+ * Tests needs safe region bounds are modified if results from the next continue have been set.
+ */
+ @Test
+ public void testContinue_needsSafeRegionBoundsModifiedFromLaterContinue() {
+ final LaunchParams params1 = new LaunchParams();
+ params1.mNeedsSafeRegionBounds = false;
+ final InstrumentedPositioner positioner1 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params1);
+
+ final LaunchParams params2 = new LaunchParams();
+ params2.mNeedsSafeRegionBounds = true;
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+
+ final LaunchParams result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+ // Safe region is propagated from positioner1
+ assertEquals(result.mNeedsSafeRegionBounds,
+ positioner1.getLaunchParams().mNeedsSafeRegionBounds);
+ assertEquals(result.mWindowingMode, positioner2.getLaunchParams().mWindowingMode);
+ assertEquals(result.mBounds, positioner2.getLaunchParams().mBounds);
+ assertEquals(result.mPreferredTaskDisplayArea,
+ positioner2.getLaunchParams().mPreferredTaskDisplayArea);
+ }
+
+ /**
+ * Tests only needs safe region bounds are propagated to result done even if there are skipped
+ * and continued results and continue sets true for needs safe region bounds.
+ */
+ @Test
+ public void testDone_ContinueSetsNeedsSafeRegionBounds() {
+ final LaunchParams params1 = new LaunchParams();
+ final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_DONE, params1);
+
+ final LaunchParams params2 = new LaunchParams();
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+ final LaunchParams params3 = new LaunchParams();
+ final InstrumentedPositioner positioner3 = new InstrumentedPositioner(RESULT_SKIP, params3);
+
+ final LaunchParams params4 = new LaunchParams();
+ params4.mNeedsSafeRegionBounds = true;
+ final InstrumentedPositioner positioner4 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params4);
+
+ final LaunchParams params5 = new LaunchParams();
+ params5.mNeedsSafeRegionBounds = false;
+ final InstrumentedPositioner positioner5 = new InstrumentedPositioner(RESULT_SKIP, params5);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+ mController.registerModifier(positioner3);
+ mController.registerModifier(positioner4);
+ mController.registerModifier(positioner5);
+
+ final LaunchParams result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+ // Safe region is propagated from positioner4
+ assertEquals(result.mNeedsSafeRegionBounds,
+ positioner4.getLaunchParams().mNeedsSafeRegionBounds);
+ assertEquals(result.mWindowingMode, positioner1.getLaunchParams().mWindowingMode);
+ assertEquals(result.mBounds, positioner1.getLaunchParams().mBounds);
+ assertEquals(result.mPreferredTaskDisplayArea,
+ positioner1.getLaunchParams().mPreferredTaskDisplayArea);
+ }
+
+ /**
+ * Tests only needs safe region bounds are set if results are done.
+ */
+ @Test
+ public void testDone_needsSafeRegionBoundsModified() {
+ final LaunchParams params = new LaunchParams();
+ params.mNeedsSafeRegionBounds = true;
+ final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+
+ mController.registerModifier(positioner);
+
+ final LaunchParams result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+ assertEquals(result, positioner.getLaunchParams());
+ }
+
+ /**
* Tests preferred display id calculation for VR.
*/
@Test
@@ -372,6 +528,34 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
assertEquals(expected, task.mLastNonFullscreenBounds);
}
+ /**
+ * Ensures that app bounds are set to exclude freeform caption if window is in freeform.
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ public void testLayoutTaskBoundsFreeformAppBounds() {
+ final Rect expected = new Rect(10, 20, 30, 40);
+
+ final LaunchParams params = new LaunchParams();
+ params.mBounds.set(expected);
+ params.mAppBounds.set(expected);
+ final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+ final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ final ActivityOptions options = ActivityOptions.makeBasic().setFlexibleLaunchSize(true);
+
+ mController.registerModifier(positioner);
+
+ assertNotEquals(expected, task.getBounds());
+
+ layoutTask(task, options);
+
+ // Task will make adjustments to requested bounds. We only need to guarantee that the
+ // requested bounds are expected.
+ assertEquals(expected,
+ task.getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
+ }
+
public static class InstrumentedPositioner implements LaunchParamsModifier {
private final int mReturnVal;
@@ -473,4 +657,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */,
null /* options */);
}
+
+ private void layoutTask(@NonNull Task task, ActivityOptions options) {
+ mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */,
+ options /* options */);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 66d7963946b9..1356718be4dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8a7e7434e604..2c6884e7a35a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -52,7 +52,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static java.lang.Integer.MAX_VALUE;
@@ -1293,7 +1293,7 @@ public class RecentTasksTest extends WindowTestsBase {
// Add secondTask to top again
mRecentTasks.add(secondTask);
- verifyZeroInteractions(controller);
+ verifyNoMoreInteractions(controller);
}
@Test
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 e2c4a1d2dfea..fc70b80790c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4738,6 +4738,213 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testIsLetterboxedForSafeRegionOnlyAllowed_noManifestProperty_returnsTrue() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+ // For an activity letterboxed only due to safe region, areBoundsLetterboxed will return
+ // false
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+ // Since no manifest property is defined, the activity is opted in by default
+ assertTrue(mActivity.mAppCompatController.getSafeRegionPolicy()
+ .isLetterboxedForSafeRegionOnlyAllowed());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testIsLetterboxedForSafeRegionOnlyAllowed_allowedForActivity_returnsTrue() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+ // Activity can opt-out the safe region letterboxing by component level property.
+ final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
+ updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, true /* value */);
+ updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ final ActivityRecord optOutActivity = new ActivityBuilder(mAtm)
+ .setComponent(name).setTask(mTask).build();
+ optOutActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+ // Since activity manifest property is defined as true, the activity can be letterboxed
+ // for safe region
+ assertTrue(optOutActivity.mAppCompatController
+ .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testIsLetterboxedForSafeRegionOnlyAllowed_notAllowedForActivity_returnsFalse() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+ final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
+ updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ final ActivityRecord optOutActivity = new ActivityBuilder(mAtm)
+ .setComponent(name).setTask(mTask).build();
+ optOutActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+ // Since activity manifest property is defined as false, the activity can not be letterboxed
+ // for safe region
+ assertFalse(optOutActivity.mAppCompatController
+ .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testIsLetterboxedForSafeRegionOnlyAllowed_notAllowedForApplication_returnsFalse() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+ final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
+ updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ final ActivityRecord optOutAppActivity = new ActivityBuilder(mAtm)
+ .setComponent(name).setTask(mTask).build();
+ optOutAppActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+ // Since application manifest property is defined as false, the activity can not be
+ // letterboxed for safe region
+ assertFalse(optOutAppActivity.mAppCompatController
+ .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testIsLetterboxedForSafeRegionOnlyAllowed_allowedForApplication_returnsTrue() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+ final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
+ updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+ updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, true /* value */);
+ final ActivityRecord optOutAppActivity = new ActivityBuilder(mAtm)
+ .setComponent(name).setTask(mTask).build();
+ optOutAppActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+ // Since application manifest property is defined as true, the activity can be letterboxed
+ // for safe region
+ assertTrue(optOutAppActivity.mAppCompatController
+ .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+ }
+
+ private void updateApplicationLevelAllowSafeRegionLetterboxingProperty(ComponentName name,
+ PackageManager pm, boolean propertyValueForApplication) {
+ final PackageManager.Property propertyForApplication = new PackageManager.Property(
+ "propertyName", /* value */ propertyValueForApplication, name.getPackageName(),
+ name.getClassName());
+ try {
+ doReturn(propertyForApplication).when(pm).getPropertyAsUser(
+ WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+ name.getPackageName(), /* className */ null, /* userId */ 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void updateActivityLevelAllowSafeRegionLetterboxingProperty(ComponentName name,
+ PackageManager pm, boolean propertyValueForActivity) {
+ final PackageManager.Property propertyForActivity = new PackageManager.Property(
+ "propertyName", /* value */ propertyValueForActivity, name.getPackageName(),
+ name.getClassName());
+ try {
+ doReturn(propertyForActivity).when(pm).getPropertyAsUser(
+ WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+ name.getPackageName(), name.getClassName(), /* userId */ 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testAreBoundsLetterboxed_letterboxedForSafeRegionAndFixedOrientation_returnTrue() {
+ setUpLandscapeLargeScreenDisplayWithApp();
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ final Rect safeRegionBounds = setupSafeRegionBoundsParameters(/* dw */ 500, /* dh */ 200);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is letterboxed due to fixed orientation within the safe region
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ assertTrue(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity,
+ APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION);
+ assertFalse(mActivity.mAppCompatController.getSafeRegionPolicy()
+ .isLetterboxedForSafeRegionOnlyAllowed());
+ assertTrue(safeRegionBounds.contains(mActivity.getBounds()));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testAreBoundsLetterboxed_letterboxedForSafeRegionAndAspectRatio_returnTrue() {
+ setUpPortraitLargeScreenDisplayWithApp();
+
+ assertFalse(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+ final Rect safeRegionBounds = setupSafeRegionBoundsParameters(/* dw */ 200, /* dh */ 300);
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is letterboxed due to min aspect ratio within the safe region
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
+ .isLetterboxedForFixedOrientationAndAspectRatio());
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
+ .isLetterboxedForAspectRatioOnly());
+ assertFalse(mActivity.inSizeCompatMode());
+ assertTrue(mActivity.areBoundsLetterboxed());
+ verifyLogAppCompatState(mActivity,
+ APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+ assertTrue(safeRegionBounds.contains(mActivity.getBounds()));
+ }
+
+ private Rect setupSafeRegionBoundsParameters(int dw, int dh) {
+ final AppCompatController appCompatController = mActivity.mAppCompatController;
+ final AppCompatSafeRegionPolicy safeRegionPolicy =
+ appCompatController.getSafeRegionPolicy();
+ safeRegionPolicy.setNeedsSafeRegionBounds(true);
+ spyOn(mTask);
+ final Rect safeRegionBounds = new Rect(100, 200, 100 + dw, 200 + dh);
+ doReturn(safeRegionBounds).when(mTask).getSafeRegionBounds();
+ return safeRegionBounds;
+ }
+
+ @Test
public void testAreBoundsLetterboxed_letterboxedForSizeCompat_returnsTrue() {
setUpDisplaySizeWithApp(1000, 2500);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index b2c195e8ebaa..547fc04f9fac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -153,7 +153,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
path = FILES_DIR.getPath() + "/snapshots/";
}
for (int i = 0; i < fileNames.length; i++) {
- files[i] = new File(path + fileNames[i]);
+ files[i] = new File(path, fileNames[i]);
}
return files;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 4f310de1e48b..e0e65e67762e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1431,6 +1431,93 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBounds_appliedOnNodeAndChildren() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+ final TestWindowContainer child11 = child1.addChildWindow();
+ final TestWindowContainer child12 = child1.addChildWindow();
+ final TestWindowContainer child21 = child2.addChildWindow();
+
+ assertNull(root.getSafeRegionBounds());
+ assertNull(child1.getSafeRegionBounds());
+ assertNull(child11.getSafeRegionBounds());
+ assertNull(child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+
+ final Rect tempSafeRegionBounds1 = new Rect(50, 50, 200, 300);
+ child1.setSafeRegionBounds(tempSafeRegionBounds1);
+
+ assertNull(root.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+
+ // Set different safe region bounds on child11
+ final Rect tempSafeRegionBounds2 = new Rect(30, 30, 200, 200);
+ child11.setSafeRegionBounds(tempSafeRegionBounds2);
+
+ assertNull(root.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds2, child11.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBounds_resetSafeRegionBounds() {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+ final TestWindowContainer child11 = child1.addChildWindow();
+ final TestWindowContainer child12 = child1.addChildWindow();
+ final TestWindowContainer child21 = child2.addChildWindow();
+
+ assertNull(root.getSafeRegionBounds());
+ assertNull(child1.getSafeRegionBounds());
+ assertNull(child11.getSafeRegionBounds());
+ assertNull(child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+
+ final Rect tempSafeRegionBounds1 = new Rect(50, 50, 200, 300);
+ child1.setSafeRegionBounds(tempSafeRegionBounds1);
+
+ assertNull(root.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+
+ // Set different safe region bounds on child11
+ final Rect tempSafeRegionBounds2 = new Rect(30, 30, 200, 200);
+ child11.setSafeRegionBounds(tempSafeRegionBounds2);
+
+ assertEquals(tempSafeRegionBounds2, child11.getSafeRegionBounds());
+
+ // Reset safe region bounds on child11. Now child11 will use child1 safe region bounds.
+ child11.setSafeRegionBounds(/* safeRegionBounds */null);
+
+ assertNull(root.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+ assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+ assertNull(child2.getSafeRegionBounds());
+ assertNull(child21.getSafeRegionBounds());
+ }
+
+ @Test
public void testSetExcludeInsetsTypes_appliedOnNodeAndChildren() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
index dcb68620e361..c0be214241a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
@@ -36,8 +36,10 @@ import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.times;
import android.content.Intent;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -46,6 +48,8 @@ import android.window.WindowContainerTransaction.HierarchyOp;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,6 +66,8 @@ import java.util.List;
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowContainerTransactionTests extends WindowTestsBase {
+ private final Rect mSafeRegionBounds = new Rect(50, 50, 200, 300);
+
@Test
public void testRemoveTask() {
final Task rootTask = createTask(mDisplayContent);
@@ -209,6 +215,152 @@ public class WindowContainerTransactionTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnTaskDisplayArea() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = taskDisplayArea.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the task display area
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(taskDisplayArea.getSafeRegionBounds(), mSafeRegionBounds);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnRootTask() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the root task
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnTask() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = task.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the task
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(rootTask.getSafeRegionBounds());
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnTask_resetSafeRegionBounds() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = task.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the task
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(rootTask.getSafeRegionBounds());
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+
+ // Reset safe region bounds on the task
+ wct.setSafeRegionBounds(token, /* safeRegionBounds */null);
+ applyTransaction(wct);
+
+ assertNull(activity.getSafeRegionBounds());
+ assertNull(task.getSafeRegionBounds());
+ assertNull(rootTask.getSafeRegionBounds());
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnRootTaskAndTask() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the root task
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ // Set different safe region bounds on task
+ final Rect tempSafeRegionBounds = new Rect(30, 30, 200, 200);
+ wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(), tempSafeRegionBounds);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), tempSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), tempSafeRegionBounds);
+ assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnRootTaskAndTask_resetSafeRegionBoundsOnTask() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+ // Set safe region bounds on the root task
+ wct.setSafeRegionBounds(token, mSafeRegionBounds);
+ // Set different safe region bounds on task
+ final Rect mTmpSafeRegionBounds = new Rect(30, 30, 200, 200);
+ wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(), mTmpSafeRegionBounds);
+ applyTransaction(wct);
+
+ // Task and activity will use different safe region bounds
+ assertEquals(activity.getSafeRegionBounds(), mTmpSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mTmpSafeRegionBounds);
+ assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+
+ // Reset safe region bounds on task
+ wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(),
+ /* safeRegionBounds */null);
+ applyTransaction(wct);
+
+ assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+ assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+ assertNull(taskDisplayArea.getSafeRegionBounds());
+ }
+
+ @Test
public void testDesktopMode_moveTaskToFront() {
final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
TaskDisplayArea tda = desktopOrganizer.mDefaultTDA;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 8606581539ff..669f5d28267d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -24,7 +24,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 5401a44d7016..6a35fa8e20ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -61,6 +61,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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;
@@ -1568,6 +1569,51 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnRootTask() {
+ Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_FULLSCREEN, null);
+ final Task task1 = createRootTask();
+ final Task task2 = createTask(rootTask, false /* fakeDraw */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ Rect safeRegionBounds = new Rect(50, 50, 200, 300);
+
+ wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(), safeRegionBounds);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+ assertEquals(rootTask.getSafeRegionBounds(), safeRegionBounds);
+ assertEquals(task2.getSafeRegionBounds(), safeRegionBounds);
+ assertNull(task1.getSafeRegionBounds());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+ public void testSetSafeRegionBoundsOnRootTask_resetSafeRegionBounds() {
+ Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_FULLSCREEN, null);
+ final Task task1 = createRootTask();
+ final Task task2 = createTask(rootTask, false /* fakeDraw */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ Rect safeRegionBounds = new Rect(50, 50, 200, 300);
+
+ wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(), safeRegionBounds);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+ assertEquals(rootTask.getSafeRegionBounds(), safeRegionBounds);
+ assertEquals(task2.getSafeRegionBounds(), safeRegionBounds);
+ assertNull(task1.getSafeRegionBounds());
+
+ // Reset safe region bounds on the root task
+ wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(),
+ /* safeRegionBounds */null);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+ assertNull(rootTask.getSafeRegionBounds());
+ assertNull(task2.getSafeRegionBounds());
+ assertNull(task1.getSafeRegionBounds());
+ }
+
+ @Test
public void testReparentToOrganizedTask() {
final ITaskOrganizer organizer = registerMockOrganizer();
Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 59ee2f5c8e9f..5624677779a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -23,9 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -88,7 +85,6 @@ import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -673,56 +669,6 @@ public class WindowStateTests extends WindowTestsBase {
}
@Test
- public void testSeamlesslyRotateWindow() {
- final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
- final SurfaceControl.Transaction t = spy(StubTransaction.class);
-
- makeWindowVisible(app);
- app.mSurfaceControl = mock(SurfaceControl.class);
- final Rect frame = app.getFrame();
- frame.set(10, 20, 60, 80);
- app.updateSurfacePosition(t);
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */);
- assertTrue(app.mSeamlesslyRotated);
-
- // Verify we un-rotate the window state surface.
- final Matrix matrix = new Matrix();
- // Un-rotate 90 deg.
- matrix.setRotate(270);
- // Translate it back to origin.
- matrix.postTranslate(0, mDisplayInfo.logicalWidth);
- verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
-
- // Verify we update the position as well.
- final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-
- app.finishSeamlessRotation(t);
- assertFalse(app.mSeamlesslyRotated);
- assertNull(app.mPendingSeamlessRotate);
-
- // Simulate the case with deferred layout and animation.
- app.resetSurfacePositionForAnimationLeash(t);
- clearInvocations(t);
- mWm.mWindowPlacerLocked.deferLayout();
- app.updateSurfacePosition(t);
- // Because layout is deferred, the position should keep the reset value.
- assertTrue(app.mLastSurfacePosition.equals(0, 0));
-
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */);
- // The last position must be updated so the surface can be unrotated properly.
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- matrix.setRotate(90);
- matrix.postTranslate(mDisplayInfo.logicalHeight, 0);
- curSurfacePos[0] = frame.left;
- curSurfacePos[1] = frame.top;
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
- }
-
- @Test
public void testVisibilityChangeSwitchUser() {
final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
window.mHasSurface = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index a02c3db1e636..8907a72e0b34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -35,16 +37,19 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
+import android.graphics.Matrix;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.window.WindowContext;
@@ -335,6 +340,31 @@ public class WindowTokenTests extends WindowTestsBase {
}
@Test
+ public void testSeamlesslyRotate() {
+ final SurfaceControl.Transaction t = mTransaction;
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ token.mLastSurfacePosition.x = 10;
+ token.mLastSurfacePosition.y = 20;
+ final SeamlessRotator rotator = new SeamlessRotator(ROTATION_0, ROTATION_90,
+ mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */);
+ clearInvocations(t);
+ rotator.unrotate(t, token);
+
+ // Verify surface is un-rotated.
+ final Matrix matrix = new Matrix();
+ // Un-rotate 90 deg.
+ matrix.setRotate(270);
+ // Translate it back to origin.
+ matrix.postTranslate(0, mDisplayInfo.logicalWidth);
+ verify(t).setMatrix(eq(token.mSurfaceControl), eq(matrix), any(float[].class));
+
+ final float[] curSurfacePos = {token.mLastSurfacePosition.x, token.mLastSurfacePosition.y};
+ matrix.mapPoints(curSurfacePos);
+ verify(t).setPosition(eq(token.mSurfaceControl),
+ eq(curSurfacePos[0]), eq(curSurfacePos[1]));
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
public void onDisplayChanged_differentDisplay_reparented() {
final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index a1d35a7d447c..0749c0b94537 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -22,7 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -118,7 +118,7 @@ public class WindowTracingLegacyTest {
@Test
public void trace_discared_whenNotTracing() {
mWindowTracing.logState("where");
- verifyZeroInteractions(mWmMock);
+ verifyNoMoreInteractions(mWmMock);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 9367941e32a3..3da279bb9663 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -22,7 +22,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -128,7 +128,7 @@ public class WindowTracingPerfettoTest {
@Test
public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
sWindowTracing.logState("where");
- verifyZeroInteractions(sWmMock);
+ verifyNoMoreInteractions(sWmMock);
}
@Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 50c5a6b68c7b..14915109999c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3918,6 +3918,22 @@ public class CarrierConfigManager {
"5g_icon_display_secondary_grace_period_string";
/**
+ * When an NR advanced connection is lost and a Physical Cell ID (PCI) change occurs within
+ * the primary timer{@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING}, delay updating the network
+ * icon.
+ *
+ * <p>This delay is implemented because a rapid PCI change often indicates the device is
+ * switching to a nearby cell tower to quickly restore the NR advanced connection. Displaying
+ * an intermediate network icon (like 4G/LTE) might be misleading if the 5G connection is
+ * restored shortly after. This value sets the delay in seconds; 0 disables the feature.</p>
+ *
+ * @hide
+ */
+ public static final String KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT =
+ "nr_advanced_pci_change_secondary_timer_seconds_int";
+
+
+ /**
* The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
* to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
*
@@ -11222,6 +11238,7 @@ public class CarrierConfigManager {
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+ sDefaults.putInt(KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT, 0);
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, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f5282639ae6c..6e23edf936c7 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -614,7 +614,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
/** @hide */
public static int convertRssiAsuToDBm(int rssiAsu) {
- if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+ if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN || rssiAsu == Integer.MAX_VALUE) {
return CellInfo.UNAVAILABLE;
}
if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e6515f13b858..850ce3e2bbdc 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,7 +38,6 @@ import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.Qos;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
@@ -89,35 +87,30 @@ public final class PreciseDataConnectionState implements Parcelable {
* Unsupported. The unsupported state is used when the data network cannot support the network
* validation function for the current data connection state.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
/**
* Not Requested. The not requested status is used when the data network supports the network
* validation function, but no network validation is being performed yet.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
/**
* In progress. The in progress state is used when the network validation process for the data
* network is in progress. This state is followed by either success or failure.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
/**
* Success. The Success status is used when network validation has been completed for the data
* network and the result is successful.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_SUCCESS = 3;
/**
* Failure. The Failure status is used when network validation has been completed for the data
* network and the result is failure.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_FAILURE = 4;
/**
@@ -360,7 +353,6 @@ public final class PreciseDataConnectionState implements Parcelable {
*
* @return the network validation status of the data call
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NetworkValidationStatus int getNetworkValidationStatus() {
return mNetworkValidationStatus;
}
@@ -615,7 +607,6 @@ public final class PreciseDataConnectionState implements Parcelable {
* @param networkValidationStatus the network validation status of the data call
* @return The builder
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NonNull Builder setNetworkValidationStatus(
@NetworkValidationStatus int networkValidationStatus) {
mNetworkValidationStatus = networkValidationStatus;
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index 7356cdc8889b..42d09cfc2e43 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -31,7 +31,6 @@ import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsManager;
import android.telephony.satellite.SatelliteManager;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
@@ -77,9 +76,6 @@ public class TelephonyFrameworkInitializer {
// also check through Compatibility framework a few lines below).
@SuppressWarnings("AndroidFrameworkCompatChange")
private static boolean hasSystemFeature(Context context, String feature) {
- // Check release status of this change in behavior.
- if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
-
// Check SDK version of the vendor partition.
final int vendorApiLevel = SystemProperties.getInt(
"ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 14d567d141cb..2983e4442a78 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -691,7 +691,7 @@ public class TelephonyManager {
case UNKNOWN:
modemCount = 1;
// check for voice and data support, 0 if not supported
- if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
modemCount = 0;
}
break;
@@ -2814,7 +2814,7 @@ public class TelephonyManager {
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getPhoneType() {
- if (!isVoiceCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isDataCapable()) {
return PHONE_TYPE_NONE;
}
return getCurrentPhoneType();
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b6f9e1f4c3af..0e7030688c70 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -17,7 +17,6 @@
package android.telephony.data;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -32,7 +31,6 @@ import android.telephony.PreciseDataConnectionState;
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -455,7 +453,6 @@ public final class DataCallResponse implements Parcelable {
*
* @return The network validation status of data connection.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
return mNetworkValidationStatus;
}
@@ -936,7 +933,6 @@ public final class DataCallResponse implements Parcelable {
* @param status The network validation status.
* @return The same instance of the builder.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NonNull Builder setNetworkValidationStatus(
@PreciseDataConnectionState.NetworkValidationStatus int status) {
mNetworkValidationStatus = status;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f04e1c9b221d..5baf46388fc4 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -17,7 +17,6 @@
package android.telephony.data;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -40,7 +39,6 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
@@ -414,7 +412,6 @@ public abstract class DataService extends Service {
* @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
* request validation to the DataService and checks if the request has been submitted.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public void requestNetworkValidation(int cid,
@NonNull @CallbackExecutor Executor executor,
@NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index f775de6ebef4..c42b29c143aa 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -17,7 +17,6 @@
package android.telephony.data;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -41,7 +40,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.flags.FeatureFlagsImpl;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
@@ -290,7 +288,6 @@ public abstract class QualifiedNetworksService extends Service {
* @param resultCodeCallback A callback to determine whether the request was successfully
* submitted or not.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public void requestNetworkValidation(
@NetCapability int networkCapability,
@NonNull @CallbackExecutor Executor executor,
@@ -298,15 +295,6 @@ public abstract class QualifiedNetworksService extends Service {
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
- if (!sFeatureFlag.networkValidation()) {
- loge("networkValidation feature is disabled");
- executor.execute(
- () ->
- resultCodeCallback.accept(
- DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
- return;
- }
-
IIntegerConsumer callback = new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
diff --git a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
index d16e90e26aaa..9d60e7c8b0b0 100644
--- a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
+++ b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
@@ -20,7 +20,7 @@ import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStat
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -123,7 +123,7 @@ public class AppLocaleCollectorTest {
doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
- anyObject(), eq(null), eq(true));
+ any(), eq(null), eq(true));
doReturn(mSystemCurrentLocales).when(
mAppLocaleCollector).getSystemCurrentLocales();
@@ -159,7 +159,7 @@ public class AppLocaleCollectorTest {
doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
- anyObject(), eq(null), eq(true));
+ any(), eq(null), eq(true));
doReturn(mSystemCurrentLocales).when(
mAppLocaleCollector).getSystemCurrentLocales();
diff --git a/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java b/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java
new file mode 100644
index 000000000000..eda5e8613dba
--- /dev/null
+++ b/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java
@@ -0,0 +1,523 @@
+/*
+ * 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;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
+import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN;
+import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.RecoverySystem;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.RescueParty.RescuePartyObserver;
+import com.android.server.am.SettingsToPropertiesMapper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Test RescueParty.
+ */
+public class RescuePartyTest {
+ private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
+
+ private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
+ private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+ private static final String PERSISTENT_PACKAGE = "com.persistent.package";
+ private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
+ private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
+ "persist.device_config.configuration.disable_rescue_party";
+ private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
+ "persist.device_config.configuration.disable_rescue_party_factory_reset";
+
+ private MockitoSession mSession;
+ private HashMap<String, String> mSystemSettingsMap;
+ private HashMap<String, String> mCrashRecoveryPropertiesMap;
+ //Records the namespaces wiped by setProperties().
+ private HashSet<String> mNamespacesWiped;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mMockContext;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PackageWatchdog mMockPackageWatchdog;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private ContentResolver mMockContentResolver;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PackageManager mPackageManager;
+
+ // Mock only sysprop apis
+ private PackageWatchdog.BootThreshold mSpyBootThreshold;
+
+ @Before
+ public void setUp() throws Exception {
+ mSession =
+ ExtendedMockito.mockitoSession().initMocks(
+ this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DeviceConfig.class)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(Settings.Global.class)
+ .spyStatic(Settings.Secure.class)
+ .spyStatic(SettingsToPropertiesMapper.class)
+ .spyStatic(RecoverySystem.class)
+ .spyStatic(RescueParty.class)
+ .spyStatic(PackageWatchdog.class)
+ .startMocking();
+ mSystemSettingsMap = new HashMap<>();
+ mNamespacesWiped = new HashSet<>();
+
+ when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+ ApplicationInfo persistentApplicationInfo = new ApplicationInfo();
+ persistentApplicationInfo.flags |=
+ ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT;
+
+ // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and
+ // system. Don't set any flags otherwise.
+ when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE),
+ anyInt())).thenReturn(persistentApplicationInfo);
+ when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE),
+ anyInt())).thenReturn(new ApplicationInfo());
+ // Reset observer instance to get new mock context on every run
+ RescuePartyObserver.reset();
+
+ // Mock SystemProperties setter and various getters
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ String value = invocationOnMock.getArgument(1);
+
+ mSystemSettingsMap.put(key, value);
+ return null;
+ }
+ ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ boolean defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue);
+ }
+ ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ int defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
+ }
+ ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ long defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Long.parseLong(storedValue);
+ }
+ ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
+
+ // Mock DeviceConfig
+ doAnswer((Answer<Boolean>) invocationOnMock -> true)
+ .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(),
+ anyBoolean()));
+ doAnswer((Answer<Void>) invocationOnMock -> null)
+ .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ DeviceConfig.Properties properties = invocationOnMock.getArgument(0);
+ String namespace = properties.getNamespace();
+ // record a wipe
+ if (properties.getKeyset().isEmpty()) {
+ mNamespacesWiped.add(namespace);
+ }
+ return true;
+ }
+ ).when(() -> DeviceConfig.setProperties(any(DeviceConfig.Properties.class)));
+
+ // Mock PackageWatchdog
+ doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
+ .when(() -> PackageWatchdog.getInstance(mMockContext));
+ mockCrashRecoveryProperties(mMockPackageWatchdog);
+
+ doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
+
+ setCrashRecoveryPropRescueBootCount(0);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+ SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSession.finishMocking();
+ }
+
+ @Test
+ public void testBootLoopNoFlags() {
+ // this is old test where the flag needs to be disabled
+ noteBoot(1);
+ assertTrue(RescueParty.isRebootPropertySet());
+
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteBoot(2);
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
+ public void testPersistentAppCrashNoFlags() {
+ // this is old test where the flag needs to be disabled
+ noteAppCrash(1, true);
+ assertTrue(RescueParty.isRebootPropertySet());
+
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteAppCrash(2, true);
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
+ public void testIsRecoveryTriggeredReboot() {
+ for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+ noteBoot(i + 1);
+ }
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteBoot(LEVEL_FACTORY_RESET + 1);
+ assertTrue(RescueParty.isRecoveryTriggeredReboot());
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
+ public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() {
+ for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+ noteBoot(i + 1);
+ }
+ int mitigationCount = LEVEL_FACTORY_RESET + 1;
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+ noteBoot(mitigationCount++);
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+ noteBoot(mitigationCount++);
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+ noteBoot(mitigationCount++);
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteBoot(mitigationCount + 1);
+ assertTrue(RescueParty.isRecoveryTriggeredReboot());
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
+ public void testThrottlingOnBootFailures() {
+ setCrashRecoveryPropAttemptingReboot(false);
+ long now = System.currentTimeMillis();
+ long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
+ for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
+ noteBoot(i);
+ }
+ assertFalse(RescueParty.isRecoveryTriggeredReboot());
+ }
+
+ @Test
+ public void testThrottlingOnAppCrash() {
+ setCrashRecoveryPropAttemptingReboot(false);
+ long now = System.currentTimeMillis();
+ long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
+ for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
+ noteAppCrash(i + 1, true);
+ }
+ assertFalse(RescueParty.isRecoveryTriggeredReboot());
+ }
+
+ @Test
+ public void testNotThrottlingAfterTimeoutOnBootFailures() {
+ setCrashRecoveryPropAttemptingReboot(false);
+ long now = System.currentTimeMillis();
+ long afterTimeout = now - TimeUnit.MINUTES.toMillis(
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
+ for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
+ noteBoot(i);
+ }
+ assertTrue(RescueParty.isRecoveryTriggeredReboot());
+ }
+
+ @Test
+ public void testNotThrottlingAfterTimeoutOnAppCrash() {
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+ setCrashRecoveryPropAttemptingReboot(false);
+ long now = System.currentTimeMillis();
+ long afterTimeout = now - TimeUnit.MINUTES.toMillis(
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
+ for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
+ noteAppCrash(i + 1, true);
+ }
+ assertTrue(RescueParty.isRecoveryTriggeredReboot());
+ }
+
+ @Test
+ public void testExplicitlyEnablingAndDisablingRescue() {
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+ SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SKIPPED);
+
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDisablingRescueByDeviceConfigFlag() {
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+ SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
+
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SKIPPED);
+
+ // Restore the property value initialized in SetUp()
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+ SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
+ }
+
+ @Test
+ public void testDisablingFactoryResetByDeviceConfigFlag() {
+ SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true));
+
+ for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+ noteBoot(i + 1);
+ }
+ assertFalse(RescueParty.isFactoryResetPropertySet());
+
+ // Restore the property value initialized in SetUp()
+ SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");
+ }
+
+ @Test
+ public void testHealthCheckLevelsNoFlags() {
+ // this is old test where the flag needs to be disabled
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+ // Ensure that no action is taken for cases where the failure reason is unknown
+ assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
+
+ // Ensure the correct user impact is returned for each mitigation count.
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+ }
+
+ @Test
+ public void testBootLoopLevelsNoFlags() {
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+ assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+ assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+ }
+
+
+ private void noteBoot(int mitigationCount) {
+ RescuePartyObserver.getInstance(mMockContext).onExecuteBootLoopMitigation(mitigationCount);
+ }
+
+ private void noteAppCrash(int mitigationCount, boolean isPersistent) {
+ String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
+ RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ new VersionedPackage(packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH,
+ mitigationCount);
+ }
+
+ // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
+ private void mockCrashRecoveryProperties(PackageWatchdog watchdog) {
+ // mock properties in RescueParty
+ try {
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_factory_reset", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isFactoryResetPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset",
+ Boolean.toString(value));
+ return null;
+ }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_reboot", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isRebootPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropAttemptingReboot(value);
+ return null;
+ }).when(() -> RescueParty.setRebootProperty(anyBoolean()));
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("persist.crashrecovery.last_factory_reset", "0");
+ return Long.parseLong(storedValue);
+ }).when(() -> RescueParty.getLastFactoryResetTimeMs());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropLastFactoryReset(value);
+ return null;
+ }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong()));
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.max_rescue_level_attempted", "0");
+ return Integer.parseInt(storedValue);
+ }).when(() -> RescueParty.getMaxRescueLevelAttempted());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted",
+ Integer.toString(value));
+ return null;
+ }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt()));
+
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while mocking crashrecovery properties " + e.getMessage());
+ }
+
+ // mock properties in BootThreshold
+ try {
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
+ mCrashRecoveryPropertiesMap = new HashMap<>();
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropRescueBootCount(count);
+ return null;
+ }).when(mSpyBootThreshold).setCount(anyInt());
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getMitigationCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count",
+ Integer.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationCount(anyInt());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setStart(anyLong());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getMitigationStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationStart(anyLong());
+
+ Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold");
+ mBootThresholdField.setAccessible(true);
+ mBootThresholdField.set(watchdog, mSpyBootThreshold);
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while spying BootThreshold " + e.getMessage());
+ }
+ }
+
+ private void setCrashRecoveryPropRescueBootCount(int count) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count",
+ Integer.toString(count));
+ }
+
+ private void setCrashRecoveryPropAttemptingReboot(boolean value) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot",
+ Boolean.toString(value));
+ }
+
+ private void setCrashRecoveryPropLastFactoryReset(long value) {
+ mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset",
+ Long.toString(value));
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 0c3c7e2af6f2..e868a6cc5a80 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -34,7 +34,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
/**
* Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -120,7 +120,7 @@ class GrantAndRevokeTrustTest {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
await()
- verifyZeroInteractions(callback)
+ verifyNoMoreInteractions(callback)
}
companion object {
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
index f5d4b0c5e345..cc7eebca1d00 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
@@ -33,7 +33,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -120,7 +120,7 @@ public class UsbServiceTest {
verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
enable, TEST_TRANSACTION_ID, mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
clearInvocations(mUsbPortManager);
clearInvocations(mCallback);
@@ -131,7 +131,7 @@ public class UsbServiceTest {
assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable,
TEST_TRANSACTION_ID, mCallback, requester, isInternalRequest));
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
clearInvocations(mUsbPortManager);
@@ -188,7 +188,7 @@ public class UsbServiceTest {
mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, TEST_SECOND_CALLER_ID, false);
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
}
@@ -203,7 +203,7 @@ public class UsbServiceTest {
verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
/**
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index fd5c4caca484..a7e0125eb3d4 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -17,8 +17,8 @@ package android.testing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
diff --git a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
index 6205b98d4e79..05f237f0002f 100644
--- a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
+++ b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
@@ -19,7 +19,7 @@ package android.os.test;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 6608dda95a4b..a34908051360 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -37,10 +37,10 @@ import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index f6123d29f35a..e1a572e7a481 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -21,7 +21,7 @@ import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 8374fd944568..7f0cabf7d159 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -24,8 +24,8 @@ import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
index 2b92428918db..0185931d628f 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -18,9 +18,9 @@ package com.android.server.vcn;
import static android.net.NetworkProvider.NetworkOfferCallback;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index bd4aeba761da..c12adcbab08a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -31,10 +31,10 @@ import static com.android.server.vcn.Vcn.VcnContentResolver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
index 27c1bc105bde..3ca84cf05cd1 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -28,7 +28,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -83,7 +82,7 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
.thenReturn(mIpSecPacketLossDetector);
when(mCarrierConfig.getIntArray(
- eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
+ eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), any()))
.thenReturn(new int[] {PENALTY_TIMEOUT_MIN});
mNetworkEvaluator = newValidUnderlyingNetworkEvaluator();
@@ -309,7 +308,7 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
public void testSetCarrierConfig() throws Exception {
final int additionalTimeoutMin = 10;
when(mCarrierConfig.getIntArray(
- eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
+ eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), any()))
.thenReturn(new int[] {PENALTY_TIMEOUT_MIN + additionalTimeoutMin});
// Update evaluator and penalize the network
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 02da835d1002..c30e7bfdfa50 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.argThat;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doNothing;