summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java8
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java20
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java70
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java18
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java49
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java9
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java8
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java28
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java33
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java26
-rw-r--r--libs/WindowManager/Shell/Android.bp1
-rw-r--r--libs/WindowManager/Shell/aconfig/Android.bp12
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig8
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml28
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml (renamed from libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml)5
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml24
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml14
-rw-r--r--libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml26
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml56
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml18
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml7
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml10
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml11
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml54
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml32
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-television/config.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-watch/colors.xml22
-rw-r--r--libs/WindowManager/Shell/res/values-watch/config.xml35
-rw-r--r--libs/WindowManager/Shell/res/values-watch/dimen.xml22
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml7
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml34
-rw-r--r--libs/WindowManager/Shell/res/values/ids.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java239
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java145
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt206
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt)17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java74
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java557
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt101
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java179
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt)21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java119
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java115
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java183
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java234
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt154
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp112
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt199
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt45
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt90
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt45
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt236
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt143
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt95
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt)4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt)8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt)4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt)4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt)8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt)11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt147
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt)14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java105
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt201
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java234
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java531
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt39
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java249
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java319
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java318
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt344
-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/DragPositioningCallbackUtilityTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java11
-rw-r--r--libs/androidfw/Android.bp1
-rw-r--r--libs/androidfw/ApkParsing.cpp5
-rw-r--r--libs/androidfw/AssetManager2.cpp390
-rw-r--r--libs/androidfw/Idmap.cpp10
-rw-r--r--libs/androidfw/LoadedArsc.cpp21
-rw-r--r--libs/androidfw/ResourceTypes.cpp108
-rw-r--r--libs/androidfw/ZipFileRO.cpp68
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h36
-rw-r--r--libs/androidfw/include/androidfw/Errors.h2
-rw-r--r--libs/androidfw/include/androidfw/IDiagnostics.h19
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h3
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h5
-rw-r--r--libs/androidfw/include/androidfw/ZipFileRO.h13
-rw-r--r--libs/androidfw/tests/ApkParsing_test.cpp6
-rw-r--r--libs/androidfw/tests/AssetManager2_bench.cpp6
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp20
-rw-r--r--libs/androidfw/tests/BenchmarkHelpers.cpp2
-rw-r--r--libs/androidfw/tests/Generic_bench.cpp146
-rw-r--r--libs/androidfw/tests/Theme_test.cpp2
-rw-r--r--libs/hwui/Android.bp17
-rw-r--r--libs/hwui/AutoBackendTextureRelease.cpp59
-rw-r--r--libs/hwui/ColorFilter.h94
-rw-r--r--libs/hwui/DisplayList.h2
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/FrameInfo.cpp26
-rw-r--r--libs/hwui/FrameInfo.h14
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp4
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp7
-rw-r--r--libs/hwui/MemoryPolicy.cpp3
-rw-r--r--libs/hwui/MemoryPolicy.h2
-rw-r--r--libs/hwui/Mesh.h25
-rw-r--r--libs/hwui/Properties.cpp2
-rw-r--r--libs/hwui/Readback.cpp31
-rw-r--r--libs/hwui/RecordingCanvas.cpp30
-rw-r--r--libs/hwui/RecordingCanvas.h4
-rw-r--r--libs/hwui/RenderNode.cpp44
-rw-r--r--libs/hwui/RenderNode.h8
-rw-r--r--libs/hwui/RenderProperties.cpp7
-rw-r--r--libs/hwui/RenderProperties.h5
-rw-r--r--libs/hwui/SkiaCanvas.h2
-rw-r--r--libs/hwui/SkiaInterpolator.cpp71
-rw-r--r--libs/hwui/SkiaInterpolator.h10
-rw-r--r--libs/hwui/SkiaWrapper.h56
-rw-r--r--libs/hwui/SkippedFrameInfo.h (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt)23
-rw-r--r--libs/hwui/Tonemapper.cpp12
-rw-r--r--libs/hwui/TreeInfo.h18
-rw-r--r--libs/hwui/aconfig/hwui_flags.aconfig22
-rw-r--r--libs/hwui/apex/android_bitmap.cpp2
-rw-r--r--libs/hwui/apex/android_canvas.cpp4
-rw-r--r--libs/hwui/apex/jni_runtime.cpp3
-rw-r--r--libs/hwui/effects/GainmapRenderer.cpp17
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp4
-rw-r--r--libs/hwui/hwui/Bitmap.cpp41
-rw-r--r--libs/hwui/hwui/Canvas.cpp2
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp3
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp5
-rw-r--r--libs/hwui/hwui/MinikinUtils.h9
-rw-r--r--libs/hwui/hwui/Typeface.cpp5
-rw-r--r--libs/hwui/jni/AnimatedImageDrawable.cpp16
-rw-r--r--libs/hwui/jni/Bitmap.cpp2
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp3
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp3
-rw-r--r--libs/hwui/jni/ColorFilter.cpp75
-rw-r--r--libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp28
-rw-r--r--libs/hwui/jni/FontFamily.cpp6
-rw-r--r--libs/hwui/jni/Graphics.cpp3
-rw-r--r--libs/hwui/jni/GraphicsJNI.h8
-rw-r--r--libs/hwui/jni/GraphicsStatsService.cpp3
-rw-r--r--libs/hwui/jni/MaskFilter.cpp12
-rw-r--r--libs/hwui/jni/NinePatch.cpp2
-rw-r--r--libs/hwui/jni/Paint.cpp108
-rw-r--r--libs/hwui/jni/RenderEffect.cpp12
-rw-r--r--libs/hwui/jni/Shader.cpp3
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp3
-rw-r--r--libs/hwui/jni/android_graphics_DisplayListCanvas.cpp8
-rw-r--r--libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp2
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp21
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp9
-rw-r--r--libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp2
-rw-r--r--libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp2
-rw-r--r--libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp11
-rw-r--r--libs/hwui/jni/fonts/Font.cpp23
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp10
-rw-r--r--libs/hwui/jni/pdf/PdfEditor.cpp3
-rw-r--r--libs/hwui/jni/pdf/PdfUtils.cpp7
-rw-r--r--libs/hwui/jni/text/GraphemeBreak.cpp3
-rw-r--r--libs/hwui/jni/text/LineBreaker.cpp83
-rw-r--r--libs/hwui/jni/text/MeasuredText.cpp19
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp51
-rw-r--r--libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp96
-rw-r--r--libs/hwui/pipeline/skia/BackdropFilterDrawable.h67
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp25
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp37
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h7
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp1
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h1
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp6
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp17
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp29
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp20
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h2
-rw-r--r--libs/hwui/pipeline/skia/StretchMask.cpp23
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp8
-rw-r--r--libs/hwui/private/hwui/DrawGlInfo.h3
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp22
-rw-r--r--libs/hwui/renderthread/CacheManager.h3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp85
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp7
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp17
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp10
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp44
-rw-r--r--libs/hwui/renderthread/VulkanManager.h5
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp8
-rw-r--r--libs/hwui/tests/common/TestUtils.h61
-rw-r--r--libs/hwui/tests/macrobench/main.cpp86
-rw-r--r--libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp2
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp16
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp36
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp2
-rw-r--r--libs/hwui/tests/unit/HintSessionWrapperTests.cpp2
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp101
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp3
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp37
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp36
-rw-r--r--libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp22
-rw-r--r--libs/hwui/tests/unit/main.cpp64
-rw-r--r--libs/input/Android.bp14
-rw-r--r--libs/input/MouseCursorController.cpp14
-rw-r--r--libs/input/PointerController.cpp58
-rw-r--r--libs/input/PointerController.h12
-rw-r--r--libs/input/PointerControllerContext.cpp8
-rw-r--r--libs/input/PointerControllerContext.h6
-rw-r--r--libs/input/SpriteController.cpp72
-rw-r--r--libs/input/SpriteController.h30
-rw-r--r--libs/input/TouchSpotController.cpp16
-rw-r--r--libs/input/tests/PointerController_test.cpp18
517 files changed, 9424 insertions, 4930 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 9da6c10c6d74..a663f9fafb50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -16,11 +16,13 @@
package androidx.window.extensions;
+import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.extensions.area.WindowAreaComponent;
@@ -104,9 +106,13 @@ public class WindowExtensionsImpl implements WindowExtensions {
* {@link WindowExtensions#getWindowLayoutComponent()}.
* @return {@link ActivityEmbeddingComponent} OEM implementation.
*/
- @NonNull
+ @Nullable
public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
if (mSplitController == null) {
+ if (!ActivityTaskManager.supportsMultiWindow(getApplication())) {
+ // Disable AE for device that doesn't support multi window.
+ return null;
+ }
synchronized (mLock) {
if (mSplitController == null) {
mSplitController = new SplitController(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 381e9d472f0f..08b7bb89d10c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -25,6 +25,7 @@ import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.window.extensions.core.util.function.Function;
/**
@@ -186,10 +187,21 @@ class SplitContainer {
return (mSplitRule instanceof SplitPlaceholderRule);
}
- @NonNull
- SplitInfo toSplitInfo() {
- return new SplitInfo(mPrimaryContainer.toActivityStack(),
- mSecondaryContainer.toActivityStack(), mCurrentSplitAttributes, mToken);
+ /**
+ * Returns the SplitInfo representing this container.
+ *
+ * @return the SplitInfo representing this container if the underlying TaskFragmentContainers
+ * are stable, or {@code null} if any TaskFragmentContainer is in an intermediate state.
+ */
+ @Nullable
+ SplitInfo toSplitInfoIfStable() {
+ final ActivityStack primaryActivityStack = mPrimaryContainer.toActivityStackIfStable();
+ final ActivityStack secondaryActivityStack = mSecondaryContainer.toActivityStackIfStable();
+ if (primaryActivityStack == null || secondaryActivityStack == null) {
+ return null;
+ }
+ return new SplitInfo(primaryActivityStack, secondaryActivityStack,
+ mCurrentSplitAttributes, mToken);
}
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 3ad30457697e..cdfc4c87d271 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -879,14 +879,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Skip resolving if the activity is on a pinned TaskFragmentContainer.
// TODO(b/243518738): skip resolving for overlay container.
- if (container != null) {
- final TaskContainer taskContainer = container.getTaskContainer();
- if (taskContainer.isTaskFragmentContainerPinned(container)) {
- return true;
- }
+ final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
+ if (container != null && taskContainer != null
+ && taskContainer.isTaskFragmentContainerPinned(container)) {
+ return true;
}
- final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
!= container) {
@@ -895,6 +893,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return true;
}
+ // Ensure the top TaskFragments are updated to the right config if activity is resolved
+ // to a new TaskFragment while pin TF exists.
+ final boolean handled = resolveActivityToContainerByRule(wct, activity, container,
+ isOnReparent);
+ if (handled && taskContainer != null) {
+ final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer();
+ if (splitPinContainer != null) {
+ final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity);
+ if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
+ @NonNull Activity activity, @Nullable TaskFragmentContainer container,
+ boolean isOnReparent) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new launched activity should always expand.
@@ -1301,6 +1321,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
+ // Ensure the top TaskFragments are updated to the right config if the intent is resolved
+ // to a new TaskFragment while pin TF exists.
+ final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct,
+ taskId, intent, launchingActivity);
+ if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) {
+ final SplitPinContainer splitPinContainer =
+ launchingContainer.getTaskContainer().getSplitPinContainer();
+ if (splitPinContainer != null) {
+ updateContainer(wct, splitPinContainer.getSecondaryContainer());
+ }
+ }
+ return launchingContainer;
+ }
+
+ /**
+ * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules.
+ */
+ @Nullable
+ TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct,
+ int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
/*
* We will check the following to see if there is any embedding rule matched:
* 1. Whether the new activity intent should always expand.
@@ -1972,8 +2012,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (mEmbeddingCallback == null || !readyToReportToClient()) {
return;
}
- final List<SplitInfo> currentSplitStates = getActiveSplitStates();
- if (mLastReportedSplitStates.equals(currentSplitStates)) {
+ final List<SplitInfo> currentSplitStates = getActiveSplitStatesIfStable();
+ if (currentSplitStates == null || mLastReportedSplitStates.equals(currentSplitStates)) {
return;
}
mLastReportedSplitStates.clear();
@@ -1983,13 +2023,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Returns a list of descriptors for currently active split states.
+ *
+ * @return a list of descriptors for currently active split states if all the containers are in
+ * a stable state, or {@code null} otherwise.
*/
@GuardedBy("mLock")
- @NonNull
- private List<SplitInfo> getActiveSplitStates() {
+ @Nullable
+ private List<SplitInfo> getActiveSplitStatesIfStable() {
final List<SplitInfo> splitStates = new ArrayList<>();
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- mTaskContainers.valueAt(i).getSplitStates(splitStates);
+ final List<SplitInfo> taskSplitStates =
+ mTaskContainers.valueAt(i).getSplitStatesIfStable();
+ if (taskSplitStates == null) {
+ return null;
+ }
+ splitStates.addAll(taskSplitStates);
}
return splitStates;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 463c8ceaf992..9a0769a82d99 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -359,11 +359,23 @@ class TaskContainer {
}
}
- /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */
- void getSplitStates(@NonNull List<SplitInfo> outSplitStates) {
+ /**
+ * Gets the descriptors of split states in this Task.
+ *
+ * @return a list of {@code SplitInfo} if all the SplitContainers are stable, or {@code null} if
+ * any SplitContainer is in an intermediate state.
+ */
+ @Nullable
+ List<SplitInfo> getSplitStatesIfStable() {
+ final List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) {
- outSplitStates.add(container.toSplitInfo());
+ final SplitInfo splitInfo = container.toSplitInfoIfStable();
+ if (splitInfo == null) {
+ return null;
+ }
+ splitStates.add(splitInfo);
}
+ return splitStates;
}
/** A wrapper class which contains the information of {@link TaskContainer} */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 61df335515b8..0a694b5c3b64 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -217,6 +217,30 @@ class TaskFragmentContainer {
/** List of non-finishing activities that belong to this container and live in this process. */
@NonNull
List<Activity> collectNonFinishingActivities() {
+ final List<Activity> activities = collectNonFinishingActivities(false /* checkIfStable */);
+ if (activities == null) {
+ throw new IllegalStateException(
+ "Result activities should never be null when checkIfstable is false.");
+ }
+ return activities;
+ }
+
+ /**
+ * Collects non-finishing activities that belong to this container and live in this process.
+ *
+ * @param checkIfStable if {@code true}, returns {@code null} when the container is in an
+ * intermediate state.
+ * @return List of non-finishing activities that belong to this container and live in this
+ * process, {@code null} if checkIfStable is {@code true} and the container is in an
+ * intermediate state.
+ */
+ @Nullable
+ List<Activity> collectNonFinishingActivities(boolean checkIfStable) {
+ if (checkIfStable
+ && (mInfo == null || mInfo.isEmpty() || !mPendingAppearedActivities.isEmpty())) {
+ return null;
+ }
+
final List<Activity> allActivities = new ArrayList<>();
if (mInfo != null) {
// Add activities reported from the server.
@@ -224,6 +248,15 @@ class TaskFragmentContainer {
final Activity activity = mController.getActivity(token);
if (activity != null && !activity.isFinishing()) {
allActivities.add(activity);
+ } else {
+ if (checkIfStable) {
+ // Return null except for a special case when the activity is started in
+ // background.
+ if (activity == null && !mTaskContainer.isVisible()) {
+ continue;
+ }
+ return null;
+ }
}
}
}
@@ -277,9 +310,19 @@ class TaskFragmentContainer {
return false;
}
- @NonNull
- ActivityStack toActivityStack() {
- return new ActivityStack(collectNonFinishingActivities(), isEmpty(), mToken);
+ /**
+ * Returns the ActivityStack representing this container.
+ *
+ * @return ActivityStack representing this container if it is in a stable state. {@code null} if
+ * in an intermediate state.
+ */
+ @Nullable
+ ActivityStack toActivityStackIfStable() {
+ final List<Activity> activities = collectNonFinishingActivities(true /* checkIfStable */);
+ if (activities == null) {
+ return null;
+ }
+ return new ActivityStack(activities, isEmpty(), mToken);
}
/** Adds the activity that will be reparented to this container. */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index a1fe7f75a826..9b84a48cdbda 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -17,6 +17,7 @@
package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.isZero;
@@ -94,14 +95,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
- /** Registers to listen to {@link CommonFoldingFeature} changes */
- public void addFoldingStateChangedCallback(
- java.util.function.Consumer<List<CommonFoldingFeature>> consumer) {
- synchronized (mLock) {
- mFoldingFeatureProducer.addDataChangedCallback(consumer);
- }
- }
-
/**
* Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
*
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index a0590dc2c832..9607b78bacf0 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -16,8 +16,10 @@
package androidx.window.extensions;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
+import android.app.ActivityTaskManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -52,7 +54,11 @@ public class WindowExtensionsTest {
@Test
public void testGetActivityEmbeddingComponent() {
- assertThat(mExtensions.getActivityEmbeddingComponent()).isNotNull();
+ if (ActivityTaskManager.supportsMultiWindow(getInstrumentation().getContext())) {
+ assertThat(mExtensions.getActivityEmbeddingComponent()).isNotNull();
+ } else {
+ assertThat(mExtensions.getActivityEmbeddingComponent()).isNull();
+ }
}
@Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index b2ffad7a74e4..d440a3eb95de 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1286,6 +1286,34 @@ public class SplitControllerTest {
}
@Test
+ public void testSplitInfoCallback_NotReportSplitIfUnstable() {
+ final Activity r0 = createMockActivity();
+ final Activity r1 = createMockActivity();
+ addSplitTaskFragments(r0, r1);
+
+ // Should report new SplitInfo list if stable.
+ mSplitController.updateCallbackIfNecessary();
+ assertEquals(1, mSplitInfos.size());
+
+ // Should not report new SplitInfo list if unstable, e.g. any Activity is finishing.
+ mSplitInfos.clear();
+ final Activity r2 = createMockActivity();
+ final Activity r3 = createMockActivity();
+ doReturn(true).when(r2).isFinishing();
+ addSplitTaskFragments(r2, r3);
+
+ mSplitController.updateCallbackIfNecessary();
+ assertTrue(mSplitInfos.isEmpty());
+
+ // Should report SplitInfo list if it becomes stable again.
+ mSplitInfos.clear();
+ doReturn(false).when(r2).isFinishing();
+
+ mSplitController.updateCallbackIfNecessary();
+ assertEquals(2, mSplitInfos.size());
+ }
+
+ @Test
public void testSplitInfoCallback_reportSplitInMultipleTasks() {
final int taskId0 = 1;
final int taskId1 = 2;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 000c65a75c81..21889960a8b2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -48,6 +48,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* Test class for {@link TaskContainer}.
*
@@ -165,4 +167,35 @@ public class TaskContainerTest {
doReturn(activity1).when(tf1).getTopNonFinishingActivity();
assertEquals(activity1, taskContainer.getTopNonFinishingActivity());
}
+
+ @Test
+ public void testGetSplitStatesIfStable() {
+ final TaskContainer taskContainer = createTestTaskContainer();
+
+ final SplitContainer splitContainer0 = mock(SplitContainer.class);
+ final SplitContainer splitContainer1 = mock(SplitContainer.class);
+ final SplitInfo splitInfo0 = mock(SplitInfo.class);
+ final SplitInfo splitInfo1 = mock(SplitInfo.class);
+ taskContainer.addSplitContainer(splitContainer0);
+ taskContainer.addSplitContainer(splitContainer1);
+
+ // When all the SplitContainers are stable, getSplitStatesIfStable() returns the list of
+ // SplitInfo representing the SplitContainers.
+ doReturn(splitInfo0).when(splitContainer0).toSplitInfoIfStable();
+ doReturn(splitInfo1).when(splitContainer1).toSplitInfoIfStable();
+
+ List<SplitInfo> splitInfoList = taskContainer.getSplitStatesIfStable();
+
+ assertEquals(2, splitInfoList.size());
+ assertEquals(splitInfo0, splitInfoList.get(0));
+ assertEquals(splitInfo1, splitInfoList.get(1));
+
+ // When any SplitContainer is in an intermediate state, getSplitStatesIfStable() returns
+ // null.
+ doReturn(null).when(splitContainer0).toSplitInfoIfStable();
+
+ splitInfoList = taskContainer.getSplitStatesIfStable();
+
+ assertNull(splitInfoList);
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 78b85e642c13..cc00a49604ee 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -321,6 +321,32 @@ public class TaskFragmentContainerTest {
}
@Test
+ public void testCollectNonFinishingActivities_checkIfStable() {
+ final TaskContainer taskContainer = createTestTaskContainer();
+ final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
+ mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+
+ // In case mInfo is null, collectNonFinishingActivities(true) should return null.
+ List<Activity> activities =
+ container.collectNonFinishingActivities(true /* checkIfStable */);
+ assertNull(activities);
+
+ // collectNonFinishingActivities(true) should return proper value when the container is in a
+ // stable state.
+ final List<IBinder> runningActivities = Lists.newArrayList(mActivity.getActivityToken());
+ doReturn(runningActivities).when(mInfo).getActivities();
+ container.setInfo(mTransaction, mInfo);
+ activities = container.collectNonFinishingActivities(true /* checkIfStable */);
+ assertEquals(1, activities.size());
+
+ // In case any activity is finishing, collectNonFinishingActivities(true) should return
+ // null.
+ doReturn(true).when(mActivity).isFinishing();
+ activities = container.collectNonFinishingActivities(true /* checkIfStable */);
+ assertNull(activities);
+ }
+
+ @Test
public void testAddPendingActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index e9abc7e522d5..c72a42cce2bd 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -158,6 +158,7 @@ android_library {
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"iconloader_base",
+ "com_android_wm_shell_flags_lib",
"WindowManager-Shell-proto",
"dagger2",
"jsr330",
diff --git a/libs/WindowManager/Shell/aconfig/Android.bp b/libs/WindowManager/Shell/aconfig/Android.bp
new file mode 100644
index 000000000000..1a98ffcea9e7
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+ name: "com_android_wm_shell_flags",
+ package: "com.android.wm.shell",
+ srcs: [
+ "multitasking.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "com_android_wm_shell_flags_lib",
+ aconfig_declarations: "com_android_wm_shell_flags",
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
new file mode 100644
index 000000000000..d55a41fc0cf7
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.wm.shell"
+
+flag {
+ name: "example_flag"
+ namespace: "multitasking"
+ description: "An Example Flag"
+ bug: "300136750"
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
new file mode 100644
index 000000000000..65f5239737b2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_focused="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_selected="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:color="@color/desktop_mode_maximize_menu_button"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml
new file mode 100644
index 000000000000..86679af5428b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_focused="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_selected="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:color="@color/desktop_mode_maximize_menu_button_outline"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
index 4ee10f429b37..15837adc2c77 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<shape android:shape="rectangle"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@android:color/white" />
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
new file mode 100644
index 000000000000..5d9fe67e8bee
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="@dimen/desktop_mode_maximize_menu_corner_radius" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml
new file mode 100644
index 000000000000..bfb0dd7f3100
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:radius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml
new file mode 100644
index 000000000000..6630fcab4794
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml
new file mode 100644
index 000000000000..7bd6e9981c12
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
index 3e0297ab612b..e9df936c3f94 100644
--- a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
+++ b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
@@ -14,8 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<vector android:height="24dp" android:tint="#000000"
- android:viewportHeight="24" android:viewportWidth="24"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorSecondary">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
</vector>
+
diff --git a/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml
new file mode 100644
index 000000000000..8ef3307ee875
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M4,18H20V6H4V18ZM22,18C22,19.1 21.1,20 20,20H4C2.9,20 2,19.1 2,18V6C2,4.9 2.9,4 4,4H20C21.1,4 22,4.9 22,6V18ZM13,8H18V14H13V8Z"
+ android:fillColor="#455A64"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
new file mode 100644
index 000000000000..b489a5c1acd0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.wm.shell.common.bubbles.BubblePopupView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/bubble_popup_margin_horizontal"
+ android:layout_marginBottom="120dp"
+ android:elevation="@dimen/bubble_manage_menu_elevation"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?android:attr/colorAccent"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_floating_landscape"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/bubble_bar_education_stack_title"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAlignment="center"
+ android:text="@string/bubble_bar_education_stack_text"/>
+
+</com.android.wm.shell.common.bubbles.BubblePopupView> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 7e0c2071dc86..fa56516e0d22 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -31,35 +31,35 @@
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
- android:paddingStart="8dp">
+ android:paddingStart="16dp">
<ImageView
android:id="@+id/application_icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_margin="4dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/app_icon_text" />
<TextView
android:id="@+id/application_name"
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="20dp"
android:minWidth="80dp"
android:textColor="@color/desktop_mode_caption_app_name_dark"
+ android:textAppearance="@android:style/TextAppearance.Material.Title"
android:textSize="14sp"
android:textFontWeight="500"
- android:gravity="center_vertical"
+ android:lineHeight="20dp"
+ android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
tools:text="Gmail"/>
<ImageButton
android:id="@+id/expand_menu_button"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:padding="4dp"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
android:contentDescription="@string/expand_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:tint="@color/desktop_mode_caption_expand_button_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index d93e9ba32105..7638132d6562 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -26,8 +26,8 @@
<ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
- android:layout_height="42dp"
- android:paddingVertical="19dp"
+ android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
+ android:paddingVertical="16dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
index 167a003932d6..c2ee3066d059 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
@@ -15,11 +15,12 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
android:orientation="horizontal"
- android:background="@drawable/desktop_mode_decor_menu_background"
+ android:background="@drawable/desktop_mode_decor_handle_menu_background"
android:gravity="center_vertical">
<ImageView
@@ -35,7 +36,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="Gmail"
- android:textColor="@color/desktop_mode_caption_menu_text_color"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textSize="14sp"
android:textFontWeight="500"
android:lineHeight="20dp"
@@ -52,6 +53,6 @@
android:contentDescription="@string/collapse_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:rotation="180"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:background="?android:selectableItemBackgroundBorderless"/>
</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
index 40a4b53f3e1d..e637671937bd 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
@@ -15,17 +15,18 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
android:orientation="vertical"
- android:background="@drawable/desktop_mode_decor_menu_background">
+ android:background="@drawable/desktop_mode_decor_handle_menu_background">
<Button
android:id="@+id/screenshot_button"
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
<Button
@@ -33,15 +34,14 @@
android:contentDescription="@string/select_text"
android:text="@string/select_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
-
<Button
android:id="@+id/close_button"
android:contentDescription="@string/close_text"
android:text="@string/close_text"
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close"
- android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
index 95283b9e214a..c4b688daeb6e 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
@@ -15,10 +15,11 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
android:orientation="horizontal"
- android:background="@drawable/desktop_mode_decor_menu_background"
+ android:background="@drawable/desktop_mode_decor_handle_menu_background"
android:gravity="center_vertical">
<ImageButton
@@ -26,7 +27,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/fullscreen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -36,7 +37,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/split_screen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -46,7 +47,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/float_button_text"
android:src="@drawable/desktop_mode_ic_handle_menu_floating"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
@@ -55,7 +56,7 @@
android:layout_marginStart="4dp"
android:contentDescription="@string/desktop_text"
android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
- android:tint="@color/desktop_mode_caption_menu_buttons_color_active"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
new file mode 100644
index 000000000000..0db72f7be8e6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_height="@dimen/desktop_mode_maximize_menu_height"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:background="@drawable/desktop_mode_maximize_menu_background">
+
+
+ <Button
+ android:id="@+id/maximize_menu_maximize_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="120dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="15dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_maximize_button_background"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:id="@+id/maximize_menu_snap_left_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="58dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="6dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_snap_left_button_background"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:id="@+id/maximize_menu_snap_right_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="58dp"
+ android:layout_height="80dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_snap_right_button_background"
+ android:stateListAnimator="@null"/>
+</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index de4a225e41a7..202ea957971e 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uit"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Het dit"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klets met borrels"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nuwe gesprekke verskyn as ikone in ’n hoek onderaan jou skerm. Tik om hulle uit te vou, of sleep om hulle toe te maak."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Beheer borrels enige tyd"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te bestuur watter apps en gesprekke in borrels kan verskyn"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tik om hierdie program te herbegin vir ’n beter aansig."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ’n beter aansig"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 21172e2267bc..4071e790d55a 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -32,23 +32,23 @@
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
<string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
- <string name="dock_forced_resizable" msgid="7429086980048964687">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
+ <string name="dock_forced_resizable" msgid="7429086980048964687">"መተግበሪያ ከተከፈለ ማያ ገፅ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም"</string>
<string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ይህ መተግበሪያ መከፈት የሚችለው በ1 መስኮት ብቻ ነው።"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"መተግበሪያ በሁለተኛ ማሳያዎች ላይ ማስጀመርን አይደግፍም።"</string>
- <string name="accessibility_divider" msgid="6407584574218956849">"የተከፈለ የማያ ገጽ ከፋይ"</string>
- <string name="divider_title" msgid="1963391955593749442">"የተከፈለ የማያ ገጽ ከፋይ"</string>
- <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"የግራ ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_divider" msgid="6407584574218956849">"የተከፈለ የማያ ገፅ ከፋይ"</string>
+ <string name="divider_title" msgid="1963391955593749442">"የተከፈለ የማያ ገፅ ከፋይ"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"የግራ ሙሉ ማያ ገፅ"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ግራ 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ግራ 50%"</string>
<string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ግራ 30%"</string>
- <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"የቀኝ ሙሉ ማያ ገጽ"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"የላይ ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"የቀኝ ሙሉ ማያ ገፅ"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"የላይ ሙሉ ማያ ገፅ"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ከላይ 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ከላይ 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ከላይ 30%"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"የታች ሙሉ ማያ ገጽ"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"የታች ሙሉ ማያ ገፅ"</string>
<string name="accessibility_split_left" msgid="1713683765575562458">"ወደ ግራ ከፋፍል"</string>
<string name="accessibility_split_right" msgid="8441001008181296837">"ወደ ቀኝ ከፋፍል"</string>
<string name="accessibility_split_top" msgid="2789329702027147146">"ወደ ላይ ከፋፍል"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ዘርጋ"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ሰብስብ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string>
@@ -76,16 +78,22 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ገባኝ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ምንም የቅርብ ጊዜ አረፋዎች የሉም"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"የቅርብ ጊዜ አረፋዎች እና የተሰናበቱ አረፋዎች እዚህ ብቅ ይላሉ"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"አረፋዎችን በመጠቀም ይወያዩ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"አዲስ ውይይቶች በማያ ገፅዎ የታችኛው ጥግ ውስጥ እንደ አዶዎች ይታያሉ። ለመዘርጋት መታ ያድርጓቸው ወይም ለማሰናበት ይጎትቷቸው።"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"በማንኛውም ጊዜ ዓረፋዎችን ይቆጣጠሩ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"የትኛዎቹ መተግበሪያዎች እና ውይይቶች ዓረፋ መፍጠር እንደሚችሉ ለማስተዳደር እዚህ ጋር መታ ያድርጉ"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ለተሻለ ዕይታ ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ።"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ለተሻለ ዕይታ ይህን መተግበሪያ እንደገና ለመጀመር መታ ያድርጉ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"የዚህን መተግበሪያ ምጥጥነ ገፅታ በቅንብሮች ውስጥ ይለውጡ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"የምጥጥነ ገፅታ ለውጥ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
- <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገጽ ሌላ መተግበሪያ ይጎትቱ"</string>
- <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቦታውን ለመቀየር ከመተግበሪያው ውጪ ሁለቴ መታ ያድርጉ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቦታውን ለመቀየር ከመተግበሪያው ውጭ ሁለቴ መታ ያድርጉ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ለተጨማሪ መረጃ ይዘርጉ።"</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ለተሻለ ዕይታ እንደገና ይጀመር?"</string>
@@ -102,11 +110,11 @@
<string name="app_icon_text" msgid="2823268023931811747">"የመተግበሪያ አዶ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string>
<string name="desktop_text" msgid="1077633567027630454">"የዴስክቶፕ ሁነታ"</string>
- <string name="split_screen_text" msgid="1396336058129570886">"የተከፈለ ማያ ገጽ"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"የተከፈለ ማያ ገፅ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
<string name="select_text" msgid="5139083974039906583">"ምረጥ"</string>
- <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገጽ እይታ"</string>
+ <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index a6be57889a4e..84c1c6763d43 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -20,7 +20,7 @@
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ሥዕል-ላይ-ሥዕል"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
<string name="pip_close" msgid="2955969519031223530">"ዝጋ"</string>
- <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገፅ"</string>
<string name="pip_move" msgid="158770205886688553">"ውሰድ"</string>
<string name="pip_expand" msgid="1051966011679297308">"ዘርጋ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ሰብስብ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index a714b49d1305..d3890a779f87 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"توسيع <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"تصغير <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"حسنًا"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ليس هناك فقاعات محادثات"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ستظهر هنا أحدث فقاعات المحادثات وفقاعات المحادثات التي تم إغلاقها."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"الدردشة باستخدام فقاعات المحادثات"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"تظهر المحادثات الجديدة في شكل رموز بأسفل أحد جانبَي الشاشة. انقر على الرمز لتوسيع المحادثة أو اسحبه لإخفائها."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"التحكّم في إظهار الفقاعات في أي وقت"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"انقر هنا للتحكّم في إظهار فقاعات التطبيقات والمحادثات التي تريدها."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"انقر لإعادة تشغيل هذا التطبيق للحصول على عرض أفضل."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"انقر لإعادة تشغيل هذا التطبيق للحصول على تجربة عرض أفضل."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"يمكنك تغيير نسبة العرض إلى الارتفاع لهذا التطبيق من خلال \"الإعدادات\"."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغيير نسبة العرض إلى الارتفاع"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 6d86747ef336..05b8f7dca729 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> সংকোচন কৰক"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুজি পালোঁ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনো শেহতীয়া bubbles নাই"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"শেহতীয়া bubbles আৰু অগ্ৰাহ্য কৰা bubbles ইয়াত প্ৰদর্শিত হ\'ব"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"নতুন বাৰ্তালাপসমূহ আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ এটা কোণত চিহ্ন হিচাপে দেখা পোৱা যায়। সেইসমূহ বিস্তাৰ কৰিবলৈ টিপক বা অগ্ৰাহ্য কৰিবলৈ টানি আনি এৰক।"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"যিকোনো সময়তে বাবল নিয়ন্ত্ৰণ কৰক"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"কোনবোৰ এপ্‌ আৰু বাৰ্তালাপ বাবল হ’ব পাৰে সেয়া পৰিচালনা কৰিবলৈ ইয়াত টিপক"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"উন্নত ভিউৰ বাবে এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"উন্নত ভিউ পোৱাৰ বাবে এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ছেটিঙলৈ গৈ এই এপ্‌টোৰ আকাৰৰ অনুপাত সলনি কৰক"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 7c662827cca7..108593e4b948 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişləndirin: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yığcamlaşdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar və buraxılmış yumrucuqlar burada görünəcək"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Yumrucuqlar vasitəsilə söhbət edin"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni söhbətlər ekranın aşağı küncündə ikonalar kimi görünür. Toxunaraq genişləndirin, yaxud sürüşdürərək imtina edin."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Yumrucuqları idarə edin"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bura toxunaraq yumrucuq göstərəcək tətbiq və söhbətləri idarə edin"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toxunaraq bu tətbiqi yenidən başladın ki, daha görüntü əldə edəsiniz."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Yaxşı görünüş üçün toxunaraq bu tətbiqi yenidən başladın"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ayarlarda bu tətbiqin tərəflər nisbətini dəyişin"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tərəflər nisbətini dəyişin"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 8de9d11def2b..76fd5b1c2d67 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ćaskajte u oblačićima"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nove konverzacije se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da biste ih proširili ili prevucite da biste ih odbacili."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolišite oblačiće u svakom trenutku"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovde i odredite koje aplikacije i konverzacije mogu da imaju oblačić"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promenite razmeru ove aplikacije u Podešavanjima"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promeni razmeru"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
@@ -89,7 +97,7 @@
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za još informacija."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Želite li da restartujete radi boljeg prikaza?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Možete da restartujete aplikaciju da bi izgledala bolje na ekranu, s tim što možete da izgubite ono što ste uradili ili nesačuvane promene, ako ih ima"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Možete da restartujete aplikaciju da bi izgledala bolje na ekranu, ali možete da izgubite napredak ili nesačuvane promene"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"Otkaži"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartuj"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovo"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 3d99514e2172..473d15acc632 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: разгарнуць"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: згарнуць"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зразумела"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Чат з выкарыстаннем усплывальных апавяшчэнняў"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новыя размовы паказваюцца ў выглядзе значкоў у ніжнім вугле экрана. Націсніце на іх, каб разгарнуць. Перацягніце іх, калі хочаце закрыць."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Кіруйце наладамі ўсплывальных апавяшчэнняў у любы час"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Каб кіраваць усплывальнымі апавяшчэннямі для праграм і размоў, націсніце тут"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Націсніце, каб перазапусціць гэту праграму для лепшага прагляду."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Націсніце, каб перазапусціць гэту праграму для зручнейшага прагляду"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змяніць суадносіны бакоў для гэтай праграмы ў наладах"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змяніць суадносіны бакоў"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 0473f270239a..7aa98e5bc218 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Преместване горе вдясно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Преместване долу вляво"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"разгъване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"свиване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Разбрах"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма скорошни балончета"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Скорошните и отхвърлените балончета ще се показват тук"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Чат с балончета"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новите разговори се показват като икони в долния ъгъл на екрана ви. Докоснете, за да ги разгънете, или ги плъзнете за отхвърляне."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Управление на балончетата по всяко време"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Докоснете тук, за да управл. кои прил. и разговори могат да показват балончета"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Докоснете, за да рестартирате това приложение с цел по-добър изглед."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Докоснете, за да рестартирате това приложение с цел по-добър изглед"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Променете съотношението на това приложение в „Настройки“"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промяна на съотношението"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 4fe1be0c455e..caad87e8f05f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"নিচে বাঁদিকে সরান"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"নিচে ডান দিকে সরান"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বড় করুন"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> আড়াল করুন"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুঝেছি"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনও সাম্প্রতিক বাবল নেই"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"সাম্প্রতিক ও বাতিল করা বাবল এখানে দেখা যাবে"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"বাবল ব্যবহার করে চ্যাট করুন"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"নতুন কথোপকথন, আপনার স্ক্রিনের নিচে কোণার দিকে আইকন হিসেবে দেখায়। সেটি বড় করতে ট্যাপ করুন বা বাতিল করতে টেনে আনুন।"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"যেকোনও সময় বাবল নিয়ন্ত্রণ করুন"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"কোন অ্যাপ ও কথোপকথনের জন্য বাবলের সুবিধা চান তা ম্যানেজ করতে এখানে ট্যাপ করুন"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"সেটিংস থেকে এই অ্যাপের অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index b39b497f5c66..66e67b385cb8 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširivanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Razumijem"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatajte koristeći oblačiće"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi razgovori se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da ih proširite ili prevucite da ih odbacite."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljajte oblačićima u svakom trenutku"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da upravljate time koje aplikacije i razgovori mogu imati oblačić"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijenite format slike aplikacije u Postavkama"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijenite format slike"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index fe76e73135c9..62399ae07e7d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entesos"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquí"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xateja amb bombolles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les converses noves es mostren com a icones en un extrem inferior de la pantalla. Toca per ampliar-les o arrossega per ignorar-les."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla les bombolles en qualsevol moment"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquí per gestionar quines aplicacions i converses poden fer servir bombolles"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Canvia la relació d\'aspecte d\'aquesta aplicació a Configuració"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Canvia la relació d\'aspecte"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 70e29705806f..8e0aba0fdec6 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žádné nedávné bubliny"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedávné bubliny a zavřené bubliny"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatujte pomocí bublin"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nové konverzace se zobrazí jako ikony v dolním rohu obrazovky. Klepnutím je rozbalíte, přetažením zavřete."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Nastavení bublin můžete kdykoli upravit"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Klepnutím sem lze spravovat, které aplikace a konverzace mohou vytvářet bubliny"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index c91cd7a956aa..d3989bcbd6f0 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"udvid <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat ved hjælp af bobler"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner nederst på din skærm. Tryk for at udvide dem, eller træk for at lukke dem."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Administrer bobler når som helst"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryk her for at administrere, hvilke apps og samtaler der kan vises i bobler"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tryk for at genstarte denne app, så visningen forbedres."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tryk for at genstarte denne app, så visningen forbedres"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Skift denne apps billedformat i Indstillinger"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Skift billedformat"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 6ce475aa3c84..5d0ee2951e90 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -20,7 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Schließen"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"„Geteilter Bildschirm“ aktivieren"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Splitscreen aktivieren"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menü „Bild im Bild“"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string>
@@ -32,8 +32,8 @@
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
<string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
- <string name="dock_forced_resizable" msgid="7429086980048964687">"Die App funktioniert bei geteiltem Bildschirm unter Umständen nicht"</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"„Geteilter Bildschirm“ wird in dieser App nicht unterstützt"</string>
+ <string name="dock_forced_resizable" msgid="7429086980048964687">"Die App funktioniert im Splitscreen-Modus unter Umständen nicht"</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Splitscreen wird in dieser App nicht unterstützt"</string>
<string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Diese App kann nur in einem einzigen Fenster geöffnet werden."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
@@ -76,15 +78,21 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles zum Chatten verwenden"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Neue Unterhaltungen erscheinen als Symbole unten auf dem Display. Du kannst sie durch Antippen maximieren und durch Ziehen schließen."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubble-Einstellungen festlegen"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tippe hier, um zu verwalten, welche Apps und Unterhaltungen als Bubble angezeigt werden können"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tippe, um diese App neu zu starten und die Ansicht zu verbessern."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tippen, um diese App neu zu starten und die Ansicht zu verbessern"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Seitenverhältnis der App in den Einstellungen ändern"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Seitenverhältnis ändern"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
- <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Weitere App hineinziehen, um den Bildschirm zu teilen"</string>
+ <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string>
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Außerhalb einer App doppeltippen, um die Position zu ändern"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string>
@@ -102,13 +110,12 @@
<string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string>
<string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
- <string name="split_screen_text" msgid="1396336058129570886">"Geteilter Bildschirm"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Splitscreen"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mehr"</string>
<string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
<string name="select_text" msgid="5139083974039906583">"Auswählen"</string>
<string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
- <!-- no translation found for expand_menu_text (3847736164494181168) -->
- <skip />
+ <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index ab5c44e9e07f..2b73528f7aef 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -66,20 +66,28 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ανάπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"σύμπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτήν την εφαρμογή."</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτή την εφαρμογή."</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Το κατάλαβα"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Δεν υπάρχουν πρόσφατα συννεφάκια"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα πρόσφατα συννεφάκια και τα συννεφάκια που παραβλέψατε θα εμφανίζονται εδώ."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Συζητήστε χρησιμοποιώντας συννεφάκια"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Οι νέες συζητήσεις εμφανίζονται ως εικονίδια σε μια από τις κάτω γωνίες της οθόνης. Πατήστε για να τις αναπτύξετε ή σύρετε για να τις παραβλέψετε."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Πατήστε εδώ για τη διαχείριση εφαρμογών και συζητήσεων που προβάλλουν συννεφάκια"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Αλλάξτε τον λόγο διαστάσεων αυτής της εφαρμογής στις Ρυθμίσεις"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Αλλαγή λόγου διαστάσεων"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index ea91014298df..2f0e898d5db7 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 01bdf95da918..a338905fa299 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Got it"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles anytime"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index ea91014298df..2f0e898d5db7 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index ea91014298df..2f0e898d5db7 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index f6dac79d0379..20344389ddcc 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎Move top right‎‏‎‎‏‎"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎Move bottom left‎‏‎‎‏‎"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎Move bottom right‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎expand ‎‏‎‎‏‏‎<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎collapse ‎‏‎‎‏‏‎<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ settings‎‏‎‎‏‎"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎Dismiss bubble‎‏‎‎‏‎"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎Don’t bubble conversation‎‏‎‎‏‎"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎Got it‎‏‎‎‏‎"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎No recent bubbles‎‏‎‎‏‎"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎Recent bubbles and dismissed bubbles will appear here‎‏‎‎‏‎"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎Chat using bubbles‎‏‎‎‏‎"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them.‎‏‎‎‏‎"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎Control bubbles anytime‎‏‎‎‏‎"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎Tap here to manage which apps and conversations can bubble‎‏‎‎‏‎"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎Tap to restart this app for a better view.‎‏‎‎‏‎"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎Tap to restart this app for a better view‎‏‎‎‏‎"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎Change this app\'s aspect ratio in Settings‎‏‎‎‏‎"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎Change aspect ratio‎‏‎‎‏‎"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎Camera issues?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap to refit‎‏‎‎‏‎"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎Didn’t fix it?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap to revert‎‏‎‎‏‎"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎No camera issues? Tap to dismiss.‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 0190ea85a30b..ebf67f6e164e 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat con burbujas"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las conversaciones nuevas aparecen como íconos en la esquina inferior de la pantalla. Presiona para expandirlas, o bien arrastra para descartarlas."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Presiona para administrar las apps y conversaciones que pueden mostrar burbujas"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Presiona para reiniciar esta app y tener una mejor vista."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Presiona para reiniciar esta app y tener una mejor vista"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta app en Configuración"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index ea44bead6766..05171b2fc6dd 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplegar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerán aquí"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea con burbujas"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las nuevas conversaciones aparecen como iconos en una esquina inferior de la pantalla. Tócalas para ampliarlas o arrástralas para cerrarlas."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas cuando quieras"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquí para gestionar qué aplicaciones y conversaciones pueden usar burbujas"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca para reiniciar esta aplicación y obtener una mejor vista."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toca para reiniciar esta aplicación y obtener una mejor vista"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta aplicación en Ajustes"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 90feff32cc2b..3af3c16ede24 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laienda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Selge"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Vestelge mullide abil"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uued vestlused kuvatakse ekraanikuva alanurgas ikoonidena. Puudutage nende laiendamiseks või lohistage neist loobumiseks."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Juhtige mulle igal ajal"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Puudutage siin, et hallata, milliseid rakendusi ja vestlusi saab mullina kuvada"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muutke selle rakenduse kuvasuhet jaotises Seaded"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Muutke kuvasuhet"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
@@ -89,7 +97,7 @@
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Kas taaskäivitada parema vaate saavutamiseks?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Saate rakenduse taaskäivitada, et see näeks ekraanikuval parem välja, kuid võite kaotada edenemise või salvestamata muudatused"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Saate rakenduse taaskäivitada, et see näeks ekraanikuval parem välja, kuid võite kaotada edenemise või salvestamata muudatused."</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"Tühista"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"Taaskäivita"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ära kuva uuesti"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index de27a8045e85..fae83883b7c4 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zabaldu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Txateatu burbuilak erabilita"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolatu burbuilak edonoiz"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Sakatu hau burbuiletan zein aplikazio eta elkarrizketa ager daitezkeen kudeatzeko"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Hobeto ikusteko, sakatu hau aplikazioa berrabiarazteko."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Hobeto ikusteko, sakatu hau, eta aplikazioa berrabiarazi egingo da"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Aldatu aplikazioaren aspektu-erlazioa ezarpenetan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Aldatu aspektu-erlazioa"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 13a2ea2db140..f7547602dcea 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -59,13 +59,15 @@
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت یک‌دستی»"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"تنظیمات برای حبابک‌های <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"سرریز"</string>
- <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"افزودن برگشت به پشته"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"افزودن برگشتن به پشته"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g> و <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> مورد بیشتر"</string>
<string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"انتقال به بالا سمت راست"</string>
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ازهم باز کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"جمع کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"گپ زدن بااستفاده از حبابک"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"مکالمه‌های جدید به‌صورت نماد در گوشه پایین صفحه‌نمایش نشان داده می‌شود. برای ازهم بازکردن آن‌ها ضربه بزنید یا برای بستن، آن‌ها را بکشید."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"کنترل حبابک‌ها در هرزمانی"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"برای مدیریت اینکه کدام برنامه‌ها و مکالمه‌ها حباب داشته باشند، ضربه بزنید"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراه‌اندازی شود."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراه‌اندازی شود"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"نسبت ابعادی این برنامه را در «تنظیمات» تغییر دهید"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغییر نسبت ابعادی"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 92fa76013134..9094c7335571 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laajenna <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistä <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Okei"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja äskettäin ohitetut kuplat näkyvät täällä"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chattaile kuplien avulla"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uudet keskustelut näkyvät kuvakkeina näytön alareunassa. Laajenna ne napauttamalla tai hylkää ne vetämällä."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Muuta kuplien asetuksia milloin tahansa"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Valitse napauttamalla tästä, mitkä sovellukset ja keskustelut voivat kuplia"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muuta tämän sovelluksen kuvasuhdetta Asetuksissa"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Vaihda kuvasuhdetta"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 7814b7d38fed..b26c1b4e3018 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer dans coin sup. droit"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer dans coin inf. gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer dans coin inf. droit"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Clavarder en utilisant des bulles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icônes dans le coin inférieur de votre écran. Touchez-les icônes pour développer les conversations ou faites-les glisser pour les supprimer."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Gérez les bulles en tout temps"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Touchez ici pour gérer les applis et les conversations à inclure aux bulles"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramètres"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index da5b5c9bfeba..6e6420a22f50 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et ignorées s\'afficheront ici"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatter via des bulles"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icônes dans l\'un des coins inférieurs de votre écran. Appuyez dessus pour les développer ou faites-les glisser pour les supprimer."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Contrôlez les bulles à tout moment"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Appuyez ici pour gérer les applis et conversations s\'affichant dans des bulles"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Appuyez pour redémarrer cette appli et avoir une meilleure vue."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Appuyez pour redémarrer cette appli et obtenir une meilleure vue."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Modifiez le format de cette appli dans les Paramètres."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier le format"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
@@ -89,7 +97,7 @@
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développez pour obtenir plus d\'informations"</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Redémarrer pour améliorer l\'affichage ?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'appli pour en améliorer son aspect sur votre écran, mais vous risquez de perdre votre progression ou les modifications non enregistrées"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'appli pour un meilleur rendu sur votre écran, mais il se peut que vous perdiez votre progression ou les modifications non enregistrées"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuler"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index c08cff86f63c..bf3a45b46484 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover á parte superior dereita"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover á parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover á parte inferior dereita"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerán aquí."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea usando burbullas"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As conversas novas móstranse como iconas nunha das esquinas inferiores da pantalla. Tócaas para amplialas ou arrástraas para pechalas."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controlar as burbullas"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca para xestionar as aplicacións e conversas que poden aparecer en burbullas"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia a proporción desta aplicación en Configuración"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar a proporción"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 2a521991ef8d..84c818230621 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"નીચે ડાબે ખસેડો"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"નીચે જમણે ખસેડો"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> મોટું કરો"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> નાનું કરો"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"સમજાઈ ગયું"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"તાજેતરના કોઈ બબલ નથી"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"એકદમ નવા બબલ અને છોડી દીધેલા બબલ અહીં દેખાશે"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"બબલનો ઉપયોગ કરીને ચૅટ કરો"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"નવી વાતચીતો તમારી સ્ક્રીનના નીચેના ખૂણામાં આઇકન તરીકે દેખાય છે. તેમને મોટી કરવા માટે, તેમના પર ટૅપ કરો અથવા તેમને છોડી દેવા માટે, તેમને ખેંચો."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"બબલને કોઈપણ સમયે નિયંત્રિત કરે છે"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"કઈ ઍપ અને વાતચીતોને બબલ કરવા માગો છો તે મેનેજ કરવા માટે, અહીં ટૅપ કરો"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"સેટિંગમાં આ ઍપનો સાપેક્ષ ગુણોત્તર બદલો"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index fb5040b36a89..8068f50d9cf6 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"बाईं ओर सबसे नीचे ले जाएं"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"सबसे नीचे दाईं ओर ले जाएं"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को बड़ा करें"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को छोटा करें"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ठीक है"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के कोई बबल्स नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल्स का इस्तेमाल करके चैट करें"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नई बातचीत, आपकी स्क्रीन पर सबसे नीचे आइकॉन के तौर पर दिखती हैं. किसी आइकॉन को बड़ा करने के लिए उस पर टैप करें या खारिज करने के लिए उसे खींचें और छोड़ें."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"जब चाहें, बबल्स की सुविधा को कंट्रोल करें"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"किसी ऐप्लिकेशन और बातचीत के लिए बबल की सुविधा को मैनेज करने के लिए यहां टैप करें"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"टैप करके ऐप्लिकेशन को रीस्टार्ट करें और बेहतर व्यू पाएं."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"बेहतर व्यू पाने के लिए, टैप करके ऐप्लिकेशन को रीस्टार्ट करें"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग में जाकर इस ऐप्लिकेशन का आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
@@ -89,7 +97,7 @@
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ज़्यादा जानकारी के लिए बड़ा करें."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"बेहतर व्यू पाने के लिए ऐप्लिकेशन को रीस्टार्ट करना है?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"स्क्रीन पर ऐप्लिकेशन का बेहतर व्यू पाने के लिए उसे रीस्टार्ट करें. हालांकि, आपने जो बदलाव सेव नहीं किए हैं या अब तक जो काम किए हैं उनका डेटा, ऐप्लिकेशन रीस्टार्ट करने पर मिट सकता है"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"स्क्रीन पर ऐप्लिकेशन का बेहतर व्यू पाने के लिए उसे रीस्टार्ट करें. हालांकि, इससे अब तक किया गया काम और सेव न किए गए बदलाव मिट सकते हैं"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"रद्द करें"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"रीस्टार्ट करें"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फिर से न दिखाएं"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 2535657c7d86..5f0b866ee4de 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Shvaćam"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje će se prikazivati nedavni i odbačeni oblačići"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Oblačići u chatu"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi se razgovori prikazuju kao ikone u donjem kutu zaslona. Dodirnite da biste ih proširili ili ih povucite da biste ih odbacili."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblačićima u svakom trenutku"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da biste odredili koje aplikacije i razgovori mogu imati oblačić"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijeni omjer slike ove aplikacije u postavkama"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijeni omjer slike"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 7566439ec7f8..d89e2923b5fe 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> kibontása"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> összecsukása"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Értem"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborékok a közelmúltból"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi és az elvetett buborékok itt jelennek majd meg"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Buborékokat használó csevegés"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Az új beszélgetések ikonokként jelennek meg a képernyő alsó sarkában. Koppintással kibonthatja, húzással pedig elvetheti őket."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Buborékok vezérlése bármikor"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ide koppintva jeleníthetők meg az alkalmazások és a beszélgetések buborékként"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Az app méretarányát a Beállításokban módosíthatja"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Méretarány módosítása"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 2b20870b1048..9c517b29d291 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Տեղափոխել վերև՝ աջ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Տեղափոխել ներքև՝ ձախ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծավալել"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծալել"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Եղավ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ամպիկներ չկան"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Զրույցի ամպիկներ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Նոր զրույցները հայտնվում են որպես պատկերակներ էկրանի ներքևի անկյունում։ Հպեք՝ դրանք ծավալելու, կամ քաշեք՝ մերժելու համար։"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ամպիկների կարգավորումներ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Հպեք այստեղ՝ ընտրելու, թե որ հավելվածների և զրույցների համար ամպիկներ ցուցադրել"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար։"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերակցությունը Կարգավորումներում"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերակցությունը"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 5747deb405ab..3dbd75087382 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"luaskan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Oke"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat dalam tampilan balon"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Percakapan baru muncul sebagai ikon di bagian pojok bawah layar. Ketuk untuk meluaskan percakapan atau tarik untuk menutup percakapan."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrol balon kapan saja"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketuk di sini untuk mengelola balon aplikasi dan percakapan"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ubah rasio aspek aplikasi ini di Setelan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ubah rasio aspek"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
@@ -88,7 +96,7 @@
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string>
- <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulai ulang untuk tampilan yang lebih baik?"</string>
+ <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulai ulang untuk melihat tampilan yang lebih baik?"</string>
<string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Anda dapat memulai ulang aplikasi agar terlihat lebih baik di layar, tetapi Anda mungkin kehilangan progres atau perubahan yang belum disimpan"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"Batal"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"Mulai ulang"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 145d26d35b75..2927d6a0d8ec 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"stækka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ég skil"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blöðrur"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blöðrur og blöðrur sem þú hefur lokað birtast hér"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Spjall með blöðrum"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Ný samtöl birtast sem tákn neðst á horni skjásins. Ýttu til að stækka þau eða dragðu til að hunsa þau."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Hægt er að stjórna blöðrum hvenær sem er"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ýttu hér til að stjórna því hvaða forrit og samtöl mega nota blöðrur."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ýta til að endurræsa forritið og fá betri sýn."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ýttu til að endurræsa forritið og fá betri sýn"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Breyta myndhlutfalli þessa forrits í stillingunum"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Breyta myndhlutfalli"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 025646cbb9cd..938a81466381 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"espandi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
@@ -76,17 +78,23 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta utilizzando le bolle"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Le nuove conversazioni vengono visualizzate sotto forma di icone in un angolo inferiore dello schermo. Tocca per espanderle o trascina per chiuderle."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Gestisci le bolle in qualsiasi momento"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tocca qui per gestire le app e le conversazioni per cui mostrare le bolle"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tocca per riavviare quest\'app per una migliore visualizzazione."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tocca per riavviare l\'app e migliorare la visualizzazione"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia le proporzioni dell\'app nelle Impostazioni"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambia proporzioni"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string>
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string>
- <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vuoi riavviare per migliorare la visualizzazione?"</string>
<string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puoi riavviare l\'app affinché venga visualizzata meglio sullo schermo, ma potresti perdere i tuoi progressi o eventuali modifiche non salvate"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index bb3845b26a88..aaa0a3866031 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"הרחבה של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"כיווץ של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"הבנתי"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"אין בועות מהזמן האחרון"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"בועות אחרונות ובועות שנסגרו יופיעו כאן"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"צ\'אט בבועות"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"שיחות חדשות מופיעות כסמלים בפינה התחתונה של המסך. אפשר להקיש כדי להרחיב אותן או לגרור כדי לסגור אותן."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"שליטה בבועות בכל זמן"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"אפשר להקיש כאן כדי לקבוע אילו אפליקציות ושיחות יוכלו להופיע בבועות"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"אפשר לשנות את יחס הגובה-רוחב של האפליקציה הזו ב\'הגדרות\'"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"שינוי יחס גובה-רוחב"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 6c1bafee9d82..1979fd52e5bb 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -54,7 +54,7 @@
<string name="accessibility_split_top" msgid="2789329702027147146">"上に分割"</string>
<string name="accessibility_split_bottom" msgid="8694551025220868191">"下に分割"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"片手モードの使用"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"終了するには、画面を下から上にスワイプするか、アプリの任意の場所をタップします"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"終了するには、画面を下から上にスワイプするか、アプリの上側の任意の場所をタップします"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"片手モードを開始します"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"片手モードを終了します"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> のバブルの設定"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を開きます"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を閉じます"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"チャットでバブルを使う"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新しい会話がアイコンとして画面下部に表示されます。タップすると開き、ドラッグして閉じることができます。"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"バブルはいつでも管理可能"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"バブルで表示するアプリや会話を管理するには、ここをタップします"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"タップしてこのアプリを再起動すると、表示が適切になります。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"タップしてこのアプリを再起動すると、表示が適切になります"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"このアプリのアスペクト比を [設定] で変更します"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"アスペクト比を変更"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index e58e67ab36cb..ce1ce6402637 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"გადაანაცვლეთ ზევით და მარჯვნივ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ქვევით და მარცხნივ გადატანა"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის გაფართოება"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის ჩაკეცვა"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"გასაგებია"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ბოლო დროს გამოყენებული ბუშტები არ არის"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"აქ გამოჩნდება ბოლოდროინდელი ბუშტები და უარყოფილი ბუშტები"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ჩეთი ბუშტების გამოყენებით"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ახალი საუბრები ხატულას სახით გამოჩნდება თქვენი ეკრანის ქვედა კუთხეში. შეეხეთ გასაფართოებლად და ჩაავლეთ მათ დასახურად."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ამოხტომის გაკონტროლება ნებისმიერ დროს"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"აქ შეეხეთ იმის სამართავად, თუ რომელი აპები და საუბრები ამოხტეს"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"შეეხეთ, რომ გადატვირთოთ ეს აპი უკეთესი ხედისთვის."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"შეხებით გადატვირთეთ ეს აპი უკეთესი ხედის მისაღებად"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"შეცვალეთ ამ აპის თანაფარდობა პარამეტრებიდან"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"თანაფარდობის შეცვლა"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 7c9120e22b30..b4b05072f4a4 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жаю"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жию"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түсінікті"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Жақындағы қалқыма хабарлар жоқ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Қалқыма хабарлар арқылы чатта сөйлесу"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Жаңа әңгімелер экранның төменгі бөлігінде белгіше түрінде көрсетіледі. Оларды жаю үшін түртіңіз, ал жабу үшін сүйреңіз."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Қалқыма хабарларды кез келген уақытта басқарыңыз"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Қалқыма хабарда көрсетілетін қолданбалар мен әңгімелерді реттеу үшін осы жерді түртіңіз."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ыңғайлы көріністі реттеу үшін қолданбаны түртіп, өшіріп қосыңыз."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Көріністі жақсарту үшін осы қолданбаны түртіп, қайта ашыңыз."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Осы қолданбаның арақатынасын параметрлерден өзгертуге болады."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Арақатынасты өзгерту"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 45302677a043..9b0a0dad5782 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង​"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោម​ខាងស្ដាំ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ពង្រីក <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"បង្រួម <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោល​ពពុះ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញ​ការសន្ទនា​ជាពពុះ"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"យល់ហើយ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"មិនមាន​ពពុះ​ថ្មីៗ​ទេ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ពពុះថ្មីៗ​ និង​ពពុះដែលបានបិទ​​នឹង​បង្ហាញ​នៅទីនេះ"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ជជែក​ដោយប្រើ​ផ្ទាំងអណ្ដែត"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ការសន្ទនាថ្មីៗបង្ហាញជារូបតំណាងនៅជ្រុងខាងក្រោមនៃអេក្រង់របស់អ្នក។ សូមចុច ដើម្បីពង្រីកការសន្ទនាទាំងនោះ ឬអូស ដើម្បីច្រានចោល។"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"គ្រប់គ្រង​ផ្ទាំងអណ្ដែតនៅពេលណាក៏បាន"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ចុចត្រង់នេះ ដើម្បីគ្រប់គ្រងកម្មវិធី និងការសន្ទនាដែលអាចបង្ហាញជាផ្ទាំងអណ្ដែត"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ។"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ផ្លាស់ប្ដូរសមាមាត្រ​របស់កម្មវិធីនេះនៅក្នុងការកំណត់"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ប្ដូរ​​សមាមាត្រ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 2dfbad49bb06..b19978aa09d7 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ಸ್ಕ್ರೀನ್‌ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ಅರ್ಥವಾಯಿತು"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಇಲ್ಲ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಮತ್ತು ವಜಾಗೊಳಿಸಿದ ಬಬಲ್ಸ್ ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಕೆಳಗಿನ ಮೂಲೆಯಲ್ಲಿ ಐಕಾನ್‌ಗಳಾಗಿ ಗೋಚರಿಸುತ್ತವೆ. ವಿಸ್ತರಿಸಲು ಅವುಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ ಅಥವಾ ವಜಾಗೊಳಿಸಲು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಬಬಲ್ಸ್ ಅನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ಯಾವ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಂಭಾಷಣೆಗಳನ್ನು ಬಬಲ್ ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ನಿರ್ವಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಈ ಆ್ಯಪ್‌ನ ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 39d717dd461a..af08da9d6e65 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"왼쪽 하단으로 이동"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"오른쪽 하단으로 이동"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 펼치기"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 접기"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"확인"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"최근 대화창 없음"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"최근 대화창과 내가 닫은 대화창이 여기에 표시됩니다."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"대화창으로 채팅하기"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"새 대화는 화면 하단에 아이콘으로 표시됩니다. 아이콘을 탭하여 펼치거나 드래그하여 닫습니다."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"언제든지 대화창을 제어하세요"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"대화창을 만들 수 있는 앱과 대화를 관리하려면 여기를 탭하세요."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"보기를 개선하려면 탭하여 앱을 다시 시작합니다."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"탭하면 앱을 다시 시작하여 보기를 개선합니다."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"설정에서 앱의 가로세로 비율을 변경합니다."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"가로세로 비율 변경"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index f210ea29da00..be24cefbaf91 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -24,7 +24,7 @@
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Сүрөт ичиндеги сүрөт менюсу"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Эгер <xliff:g id="NAME">%s</xliff:g> колдонмосу бул функцияны пайдаланбасын десеңиз, жөндөөлөрдү ачып туруп, аны өчүрүп коюңуз."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Эгер <xliff:g id="NAME">%s</xliff:g> колдонмосу бул функцияны пайдаланбасын десеңиз, параметрлерди ачып туруп, аны өчүрүп коюңуз."</string>
<string name="pip_play" msgid="3496151081459417097">"Ойнотуу"</string>
<string name="pip_pause" msgid="690688849510295232">"Тындыруу"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жайып көрсөтүү"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жыйыштыруу"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Жаңы сүйлөшүүлөр экраныңыздын төмөнкү бурчунда сүрөтчөлөр катары көрүнөт. Аларды жайып көрсөтүү үчүн таптап же четке кагуу үчүн сүйрөңүз."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Калкып чыкма билдирме түрүндө көрүнө турган колдонмолор менен маектерди тандоо үчүн бул жерди таптаңыз"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Бул колдонмонун тараптарынын катнашын параметрлерден өзгөртүү"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Тараптардын катнашын өзгөртүү"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a25699fa4d10..41219eec986c 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ຂະຫຍາຍ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ຫຍໍ້ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ລົງ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ບໍ່ມີຟອງຫຼ້າສຸດ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ຟອງຫຼ້າສຸດ ແລະ ຟອງທີ່ປິດໄປຈະປາກົດຢູ່ບ່ອນນີ້"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ການສົນທະນາໃໝ່ໆຈະປາກົດເປັນໄອຄອນຢູ່ມຸມລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານ. ແຕະເພື່ອຂະຫຍາຍ ຫຼື ລາກເພື່ອປິດໄວ້."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ຄວບຄຸມຟອງໄດ້ທຸກເວລາ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ແຕະບ່ອນນີ້ເພື່ອຈັດການແອັບ ແລະ ການສົນທະນາທີ່ສາມາດສະແດງເປັນແບບຟອງໄດ້"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ປ່ຽນອັດຕາສ່ວນຂອງແອັບນີ້ໃນການຕັ້ງຄ່າ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ປ່ຽນອັດຕາສ່ວນ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index d893fcf21d46..b98fce8f8a3f 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"išskleisti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Supratau"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nėra naujausių burbulų"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Pokalbis naudojant burbulus"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nauji pokalbiai rodomi kaip piktogramos apatiniame ekrano kampe. Palieskite piktogramą, jei norite išplėsti pokalbį, arba nuvilkite, kad atsisakytumėte."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bet kada valdyti burbulus"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Palietę čia valdykite, kurie pokalbiai ir programos gali būti rodomi burbuluose"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Pakeiskite šios programos kraštinių santykį"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Keisti kraštinių santykį"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index a1fbcddb1e3a..11b645ef80b7 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Izvērst “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Labi"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvērtu burbuļu"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Šeit būs redzami nesen rādītie burbuļi un aizvērtie burbuļi"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Tērzēšana, izmantojot burbuļus"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Jaunas sarunas tiek parādītas kā ikonas jūsu ekrāna apakšējā stūrī. Varat pieskarties, lai tās izvērstu, vai vilkt, lai tās noraidītu."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Pārvaldīt burbuļus jebkurā laikā"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Pieskarieties šeit, lai pārvaldītu, kuras lietotnes un sarunas var rādīt burbulī"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Iestatījumos mainiet šīs lietotnes malu attiecību."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mainīt malu attiecību"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 427433c7f2d4..ba2c97a4f29f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"прошири <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"собери <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Сфатив"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема неодамнешни балончиња"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Разговор со балончиња"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новите разговори се појавуваат како икони во долниот агол од екранот. Допрете за да ги проширите или повлечете за да ги отфрлите."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контролирајте ги балончињата во секое време"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Допрете тука за да одредите на кои апл. и разговори може да се појават балончиња"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Допрете за да ја рестартирате апликацијава за подобар приказ."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Допрете за да ја рестартирате апликацијава за подобар приказ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промени го соодносот на апликацијава во „Поставки“"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Променување на соодносот"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 5cca248e0dd4..8a95d7ef157c 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ചുരുക്കുക"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"പുതിയ സംഭാഷണങ്ങൾ, നിങ്ങളുടെ സ്ക്രീനിന്റെ താഴെ മൂലയിൽ ഐക്കണുകളായി ദൃശ്യമാകും. വികസിപ്പിക്കാൻ അവയിൽ ടാപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ഡിസ്‌മിസ് ചെയ്യാൻ വലിച്ചിടുക."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ഏതൊക്കെ ആപ്പുകളും സംഭാഷണങ്ങളും ബബിൾ ചെയ്യാനാകുമെന്നത് മാനേജ് ചെയ്യാൻ ഇവിടെ ടാപ്പ് ചെയ്യുക"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"മികച്ച കാഴ്‌ചയ്‌ക്കായി ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"മികച്ച കാഴ്‌ചയ്‌ക്കായി ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ഈ ആപ്പിന്റെ വീക്ഷണ അനുപാതം, ക്രമീകരണത്തിൽ മാറ്റുക"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 72e54fcf8711..bead7fe0d1c7 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г дэлгэх"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г хураах"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ойлголоо"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Саяхны бөмбөлөг алга байна"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Саяхны бөмбөлгүүд болон үл хэрэгссэн бөмбөлгүүд энд харагдана"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Бөмбөлгүүд ашиглан чатлах"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Шинэ харилцан ярианууд таны дэлгэцийн доод буланд дүрс тэмдэг байдлаар харагдана. Тэдгээрийг дэлгэхийн тулд товших эсвэл хаахын тулд чирнэ үү."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Бөмбөлгүүдийг хүссэн үедээ хянах"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ямар апп болон харилцан ярианууд бөмбөлгөөр харагдахыг энд удирдана уу"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Энэ аппын харьцааг Тохиргоонд өөрчилнө үү"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Харьцааг өөрчлөх"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index a9e6319a2110..ca0e15a0241b 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"तळाशी डावीकडे हलवा"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"तळाशी उजवीकडे हलवा"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> विस्तार करा"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोलॅप्स करा"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"समजले"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"अलीकडील कोणतेही बबल नाहीत"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल वापरून चॅट करा"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नवीन संभाषणे ही तुमच्या स्क्रीनच्या तळाशी असलेल्या कोपऱ्यात आयकनच्या स्वरूपात दिसतात. त्यांचा विस्तार करण्यासाठी टॅप करा किंवा ती डिसमिस करण्यासाठी ड्रॅग करा."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"बबल कधीही नियंत्रित करा"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"कोणती ॲप्स आणि संभाषणे बबल होऊ शकतात हे व्यवस्थापित करण्यासाठी येथे टॅप करा"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"अधिक चांगल्या व्ह्यूसाठी हे अ‍ॅप रीस्टार्ट करण्याकरिता टॅप करा."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"अधिक चांगल्या दृश्यासाठी हे अ‍ॅप रीस्टार्ट करण्याकरिता टॅप करा"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग्ज मध्ये या ॲपचा आस्पेक्ट रेशो बदला"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आस्पेक्ट रेशो बदला"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index b47531711863..fd9fb87b918c 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"kembangkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bersembang menggunakan gelembung"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Perbualan baharu dipaparkan sebagai ikon pada penjuru sebelah bawah skrin anda. Ketik untuk mengembangkan perbualan atau seret untuk mengetepikan perbualan."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kawal gelembung pada bila-bila masa"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketik di sini untuk mengurus apl dan perbualan yang boleh menggunakan gelembung"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Tukar nisbah bidang apl ini dalam Tetapan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tukar nisbah bidang"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index cb6a1df95b33..42bce8104a5c 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချဲ့ရန်"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချုံ့ရန်"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"နားလည်ပြီ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"လတ်တလော ပူဖောင်းကွက်များ မရှိပါ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"လတ်တလော ပူဖောင်းကွက်များနှင့် ပိတ်လိုက်သော ပူဖောင်းကွက်များကို ဤနေရာတွင် မြင်ရပါမည်"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ပူဖောင်းကွက်သုံး၍ ချတ်လုပ်ခြင်း"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"စကားဝိုင်းအသစ်များကို သင့်ဖန်သားပြင် အောက်ခြေထောင့်တွင် သင်္ကေတများအဖြစ် မြင်ရပါမည်။ ၎င်းတို့ကို ဖြန့်ရန်တို့ပါ (သို့) ပယ်ရန်ဖိဆွဲပါ။"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ပူဖောင်းကွက်ကို အချိန်မရွေး ထိန်းချုပ်ရန်"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ပူဖောင်းကွက်သုံးနိုင်သည့် အက်ပ်နှင့် စကားဝိုင်းများ စီမံရန် ဤနေရာကို တို့ပါ"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့နိုင်သည်။"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့ပါ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ဆက်တင်များတွင် ဤအက်ပ်၏အချိုးအစားကို ပြောင်းရန်"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"အချိုးစား ပြောင်းရန်"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 6c80144fee18..cd656b846ccf 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vis <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat med bobler"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner i et hjørne nede på skjermen. Trykk for å åpne dem, eller dra for å lukke dem."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontroller bobler når som helst"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trykk her for å administrere hvilke apper og samtaler som kan vises i bobler"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Trykk for å starte denne appen på nytt for bedre visning."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index f9f580530f8b..3300bc68c084 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाइयोस्"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"बुझेँ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हालैका बबलहरू छैनन्"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हालैका बबल र खारेज गरिएका बबलहरू यहाँ देखिने छन्"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल प्रयोग गरी कुराकानी गर्नुहोस्"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"नयाँ वार्तालापहरू तपाईंको डिभाइसको स्क्रिनको पुछारको कुनामा आइकनका रूपमा देखिन्छन्। ती आइकन ठुलो बनाउन ट्याप गर्नुहोस् वा ती आइकन हटाउन ड्र्याग गर्नुहोस्।"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"जुनसुकै बेला बबलसम्बन्धी सुविधा नियन्त्रण गर्नुहोस्"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"कुन एप र कुराकानी बबल प्रयोग गर्न सक्छन् भन्ने कुराको व्यवस्थापन गर्न यहाँ ट्याप गर्नुहोस्"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"यो एप अझ राम्रो हेर्न मिल्ने बनाउनका लागि यसलाई रिस्टार्ट गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"अझ राम्रो भ्यू प्राप्त गर्नका लागि यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस्"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिङमा गई यो एपको एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 3064cccba7cc..0d174049dc15 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uitvouwen"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatten met bubbels"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nieuwe gesprekken verschijnen als iconen in een benedenhoek van je scherm. Tik om ze uit te vouwen of sleep om ze te sluiten."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubbels beheren wanneer je wilt"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te beheren welke apps en gesprekken als bubbel kunnen worden getoond"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tik om deze app opnieuw op te starten voor een betere weergave."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tik om deze app opnieuw op te starten voor een betere weergave"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Wijzig de beeldverhouding van deze app in Instellingen"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Beeldverhouding wijzigen"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index e4c70537c452..916d8638436c 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -26,7 +26,7 @@
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"ଛବି-ଭିତରେ-ଛବି\"ରେ ଅଛି"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"ଏହି ବୈଶିଷ୍ଟ୍ୟ <xliff:g id="NAME">%s</xliff:g> ବ୍ୟବହାର ନକରିବାକୁ ଯଦି ଆପଣ ଚାହାଁନ୍ତି, ସେଟିଙ୍ଗ ଖୋଲିବାକୁ ଟାପ୍‍ କରନ୍ତୁ ଏବଂ ଏହା ଅଫ୍‍ କରିଦିଅନ୍ତୁ।"</string>
<string name="pip_play" msgid="3496151081459417097">"ପ୍ଲେ କରନ୍ତୁ"</string>
- <string name="pip_pause" msgid="690688849510295232">"ପଜ୍‍ କରନ୍ତୁ"</string>
+ <string name="pip_pause" msgid="690688849510295232">"ବିରତ କରନ୍ତୁ"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ବୁଝିଗଲି"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ବର୍ତ୍ତମାନ କୌଣସି ବବଲ୍ ନାହିଁ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ବର୍ତ୍ତମାନର ଏବଂ ଖାରଜ କରାଯାଇଥିବା ବବଲଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଆପଣଙ୍କ ସ୍କ୍ରିନର ଏକ ନିମ୍ନ କୋଣରେ ଆଇକନଗୁଡ଼ିକ ଭାବେ ଦେଖାଯାଏ। ସେଗୁଡ଼ିକୁ ବିସ୍ତାର କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ କିମ୍ବା ସେଗୁଡ଼ିକୁ ଖାରଜ କରିବା ପାଇଁ ଡ୍ରାଗ କରନ୍ତୁ।"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ଯେ କୌଣସି ସମୟରେ ବବଲଗୁଡ଼ିକ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"କେଉଁ ଆପ୍ସ ଓ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ବବଲ ହୋଇପାରିବ ତାହା ପରିଚାଳନା କରିବାକୁ ଏଠାରେ ଟାପ କରନ୍ତୁ"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ସେଟିଂସରେ ଏହି ଆପର ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index d9f7f340a889..5c936e069f9e 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ਸਮਝ ਲਿਆ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ਕੋਈ ਹਾਲੀਆ ਬਬਲ ਨਹੀਂ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ਹਾਲੀਆ ਬਬਲ ਅਤੇ ਖਾਰਜ ਕੀਤੇ ਬਬਲ ਇੱਥੇ ਦਿਸਣਗੇ"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ਬਬਲ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਚੈਟ ਕਰੋ"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ਨਵੀਂਆਂ ਗੱਲਾਂਬਾਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕੋਨੇ ਵਿੱਚ ਪ੍ਰਤੀਕਾਂ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਖਦੀਆਂ ਹਨ। ਉਨ੍ਹਾਂ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਜਾਂ ਉਨ੍ਹਾਂ ਨੂੰ ਖਾਰਜ ਕਰਨ ਲਈ ਘਸੀਟੋ।"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ਬਬਲ ਦੀ ਸੁਵਿਧਾ ਨੂੰ ਕਿਸੇ ਵੀ ਵੇਲੇ ਕੰਟਰੋਲ ਕਰੋ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ਇਹ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇੱਥੇ ਟੈਪ ਕਰੋ ਕਿ ਕਿਹੜੀਆਂ ਐਪਾਂ ਅਤੇ ਗੱਲਾਂਬਾਤਾਂ ਬਬਲ ਹੋ ਸਕਦੀਆਂ ਹਨ"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਲਈ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਵਾਸਤੇ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਇਸ ਐਪ ਦੇ ਆਕਾਰ ਅਨੁਪਾਤ ਨੂੰ ਬਦਲੋ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 0699f5df99ab..abdb5f7eb7ac 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Przenieś w prawy górny róg"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Przenieś w lewy dolny róg"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj będą pojawiać się ostatnie i odrzucone dymki"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Czatuj, korzystając z dymków"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nowe rozmowy pojawiają się jako ikony w dolnym rogu ekranu. Kliknij, aby je rozwinąć, lub przeciągnij, aby je zamknąć."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Zarządzaj dymkami, kiedy chcesz"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Kliknij tutaj, aby zarządzać wyświetlaniem aplikacji i rozmów jako dymków"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Kliknij, aby zrestartować aplikację i zyskać lepszą widoczność."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Kliknij w celu zrestartowania aplikacji, aby lepiej się wyświetlała."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmień proporcje obrazu aplikacji w Ustawieniach"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmień proporcje obrazu"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index eea9be26f432..bb68b28075bb 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balões"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como ícones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões a qualquer momento"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balões"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index ed0cdb61dacf..2c03c543d1dc 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -24,7 +24,7 @@
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu de ecrã no ecrã"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"A app <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Se não pretende que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
<string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e ignorados vão aparecer aqui."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse no chat através de balões"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As novas conversas aparecem como ícones no canto inferior do ecrã. Toque para as expandir ou arraste para as ignorar."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões em qualquer altura"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerir que apps e conversas podem aparecer em balões"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ficar com uma melhor visão."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar esta app e ficar com uma melhor visão"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Altere o formato desta app nas Definições"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Altere o formato"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index eea9be26f432..bb68b28075bb 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balões"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como ícones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balões a qualquer momento"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balões"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 8a64b1686543..579046b3a45f 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"extinde <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrânge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișa conversația în balon"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat cu baloane"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Conversațiile noi apar ca pictograme în colțul de jos al ecranului. Atinge pentru a le extinde sau trage pentru a le închide."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controlează baloanele oricând"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Atinge aici pentru a gestiona aplicațiile și conversațiile care pot apărea în balon"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Atinge ca să repornești aplicația pentru o vizualizare mai bună."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Atinge ca să repornești aplicația pentru o vizualizare mai bună"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Schimbă raportul de dimensiuni al aplicației din Setări"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Schimbă raportul de dimensiuni"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ai probleme cu camera foto?\nAtinge pentru a reîncadra"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index a7db44dda901..a8a914cbf9fc 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перенести в правый верхний угол"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перенести в левый нижний угол"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перенести в правый нижний угол"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Развернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ОК"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нет недавних всплывающих чатов"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Всплывающие чаты"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новые чаты появляются в виде значков в нижней части экрана. Коснитесь их, чтобы развернуть. Перетащите их, если хотите закрыть."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Всплывающие чаты"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Укажите приложения и разговоры, для которых разрешены всплывающие чаты."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Нажмите, чтобы перезапустить приложение и настроить удобный для просмотра вид"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Нажмите, чтобы перезапустить приложение и оптимизировать размер"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Изменить соотношение сторон приложения в настройках"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Изменить соотношение сторон"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 4153ce2bcf1f..968bc2c3b862 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> දිග හරින්න"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> හකුළන්න"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"තේරුණා"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"මෑත බුබුලු නැත"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"මෑත බුබුලු සහ ඉවත ලූ බුබුලු මෙහි දිස් වනු ඇත"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"නව සංවාද ඔබේ තිරයෙහි පහළ කෙළවරේ නිරූපක ලෙස දිස් වේ. ඒවා පුළුල් කිරීමට තට්ටු කරන්න හෝ ඒවා ඉවත දැමීමට අදින්න."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ඕනෑම වේලාවක බුබුලු පාලනය කරන්න"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"බුබුලු කළ හැකි යෙදුම් සහ සංවාද කළමනාකරණය කිරීමට මෙහි තට්ටු කරන්න"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"වඩා හොඳ දසුනක් ලබා ගැනීම සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"වඩා හොඳ දසුනක් සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"සැකසීම් තුළ මෙම යෙදුමේ දර්ශන අනුපාතය වෙනස් කරන්න"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4e38943662ea..303d81b17568 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Dobre"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedávne bubliny"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budú zobrazovať nedávne a zavreté bubliny"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Čet pomocou bublín"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nové konverzácie sa zobrazujú ako ikony v dolnom rohu obrazovky. Klepnutím ich rozbalíte a presunutím zavriete."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ovládajte bubliny kedykoľvek"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Klepnite tu a spravujte, ktoré aplikácie a konverzácie môžu ovládať bubliny"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmeniť pomer strán tejto aplikácie v Nastaveniach"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmeniť pomer strán"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index b0e67a771653..6178a1a9a489 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"razširitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"V redu"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblačkov"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuščeni oblački"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klepet z oblački"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi pogovori so prikazani kot ikone v enem od spodnjih kotov zaslona. Z dotikom pogovore razširite, z vlečenjem jih opustite."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblačkov"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dotaknite se tukaj za upravljanje aplikacij in pogovorov, ki so lahko prikazani v oblačkih"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Razmerje stranic te aplikacije spremenite v nastavitvah."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Sprememba razmerja stranic"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 29bfb9268f47..e155c72bf297 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zgjero <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"E kuptova"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska të fundit"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do të shfaqen këtu"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bisedo duke përdorur flluskat"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Bisedat e reja shfaqen si ikona në këndin e poshtëm të ekranit tënd. Trokit për t\'i zgjeruar ose zvarrit për t\'i hequr ato."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrollo flluskat në çdo moment"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trokit këtu për të menaxhuar aplikacionet e bisedat që do të shfaqen në flluska"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Trokit për të rifilluar këtë aplikacion për një pamje më të mirë."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Trokit për ta rinisur këtë aplikacion për një pamje më të mirë"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ndrysho raportin e pamjes së këtij aplikacioni te \"Cilësimet\""</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ndrysho raportin e pamjes"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 307efc9d6a2e..0e70a47f3fcb 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"проширите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"скупите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Важи"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ћаскајте у облачићима"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Нове конверзације се појављују као иконе у доњем углу екрана. Додирните да бисте их проширили или превуците да бисте их одбацили."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контролишите облачиће у сваком тренутку"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Додирните овде и одредите које апликације и конверзације могу да имају облачић"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Додирните да бисте рестартовали ову апликацију ради бољег приказа."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Додирните да бисте рестартовали ову апликацију ради бољег приказа"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промените размеру ове апликације у Подешавањима"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промени размеру"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
@@ -89,7 +97,7 @@
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширите за још информација."</string>
<string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Желите ли да рестартујете ради бољег приказа?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Можете да рестартујете апликацију да би изгледала боље на екрану, с тим што можете да изгубите оно што сте урадили или несачуване промене, ако их има"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Можете да рестартујете апликацију да би изгледала боље на екрану, али можете да изгубите напредак или несачуване промене"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"Откажи"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"Рестартуј"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Не приказуј поново"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 33652cd74fda..4c22964e125f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"utöka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas här"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta med bubblor"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nya konversationer visas som ikoner nere i hörnet på skärmen. Tryck för att utöka eller dra för att stänga dem."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Styr bubblor när som helst"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryck här för att hantera vilka appar och konversationer som får visas i bubblor"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tryck för att starta om appen och få en bättre vy."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tryck för att starta om appen och få en bättre vy"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ändra appens bildformat i inställningarna"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ändra bildformat"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index fe2ad1f1846d..71aeb61538cd 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"panua <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Nimeelewa"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Piga gumzo ukitumia viputo"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Mazungumzo mapya huonekana kama aikoni katika kona ya chini ya skrini yako. Gusa ili uyapanue au buruta ili uyaondoe."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Dhibiti viputo wakati wowote"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Gusa hapa ili udhibiti programu na mazungumzo yanayoweza kutumia viputo"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Badilisha uwiano wa programu hii katika Mipangilio"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Badilisha uwiano wa kipengele"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index fd5f0e646d9b..aab05daca0ae 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"கீழே இடப்புறமாக நகர்த்து"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"கீழே வலதுபுறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"சரி"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"சமீபத்திய குமிழ்கள் இல்லை"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"சமீபத்திய குமிழ்களும் நிராகரிக்கப்பட்ட குமிழ்களும் இங்கே தோன்றும்"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"குமிழ்களைப் பயன்படுத்தி உரையாடுங்கள்"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"புதிய உரையாடல்கள் உங்கள் திரையின் கீழ் மூலையில் ஐகான்களாகத் தோன்றும். அவற்றை விரிவாக்க தட்டவும் அல்லது நிராகரிக்க இழுக்கவும்."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"எப்போது வேண்டுமானாலும் குமிழ்களைக் கட்டுப்படுத்துங்கள்"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"எந்தெந்த ஆப்ஸும் உரையாடல்களும் குமிழியாகலாம் என்பதை நிர்வகிக்க இங்கே தட்டுங்கள்"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"அமைப்புகளில் இந்த ஆப்ஸின் தோற்ற விகிதத்தை மாற்றும்"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"தோற்ற விகிதத்தை மாற்றும்"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 6f95aa9c8305..1e407bf255de 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> విస్తరించండి"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‌ను కుదించండి"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్‌లు"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్‌ను విస్మరించు"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"అర్థమైంది"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"బబుల్స్‌ను ఉపయోగించి చాట్ చేయండి"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"కొత్త సంభాషణలు మీ స్క్రీన్ కింద మూలన చిహ్నాలుగా కనిపిస్తాయి. ట్యాప్ చేసి వాటిని విస్తరించండి లేదా లాగి విస్మరించండి."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"బబుల్స్‌ను ఎప్పుడైనా కంట్రోల్ చేయండి"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ఏ యాప్‌లు, సంభాషణలను బబుల్ చేయాలో మేనేజ్ చేయడానికి ఇక్కడ ట్యాప్ చేయండి"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"మెరుగైన వీక్షణ కోసం ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"మెరుగైన వీక్షణ కోసం ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"సెట్టింగ్‌లలో ఈ యాప్ ఆకార నిష్పత్తిని మార్చండి"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ఆకార నిష్పత్తిని మార్చండి"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index cc0333efd82b..da8abde8407f 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -45,11 +45,16 @@
<integer name="config_pipForceCloseDelay">5000</integer>
<!-- Animation duration when exit starting window: fade out icon -->
- <integer name="starting_window_app_reveal_icon_fade_out_duration">0</integer>
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
- <!-- Animation duration when exit starting window: reveal app -->
+ <!-- Animation delay when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_delay">0</integer>
<!-- Animation duration when exit starting window: reveal app -->
- <integer name="starting_window_app_reveal_anim_duration">0</integer>
+ <integer name="starting_window_app_reveal_anim_duration">500</integer>
+
+ <!-- Default animation type when hiding the starting window. The possible values are:
+ - 0 for radial vanish + slide up
+ - 1 for fade out -->
+ <integer name="starting_window_exit_animation_type">1</integer>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 6733940fd445..77a9f4b3ec76 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขาวล่าง"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ขยาย <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"รับทราบ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ไม่มีบับเบิลเมื่อเร็วๆ นี้"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"บับเบิลที่แสดงและที่ปิดไปเมื่อเร็วๆ นี้จะปรากฏที่นี่"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"แชทโดยใช้บับเบิล"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"การสนทนาครั้งใหม่ๆ จะปรากฏเป็นไอคอนที่มุมล่างของหน้าจอ โดยสามารถแตะเพื่อขยายหรือลากเพื่อปิด"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ควบคุมบับเบิลได้ทุกเมื่อ"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"แตะที่นี่เพื่อจัดการแอปและการสนทนาที่แสดงเป็นบับเบิลได้"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"เปลี่ยนสัดส่วนภาพของแอปนี้ในการตั้งค่า"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"เปลี่ยนอัตราส่วนกว้างยาว"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 8cf4eb484378..757da92ef72c 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"I-expand ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Mag-chat gamit ang mga bubble"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Lalabas ang mga bagong pag-uusap bilang mga icon sa sulok sa ibaba ng iyong screen. I-tap para i-expand ang mga ito o i-drag para i-dismiss ang mga ito."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolin ang mga bubble anumang oras"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Mag-tap dito para pamahalaan ang mga app at conversion na puwedeng mag-bubble"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"I-tap para i-restart ang app na ito para sa mas magandang view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"I-tap para i-restart ang app na ito para sa mas magandang view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Baguhin ang aspect ratio ng app na ito sa Mga Setting"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Baguhin ang aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 1454435b3de7..9c4bf8df9308 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişlet: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattığınız baloncuklar burada görünür"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Baloncukları kullanarak sohbet edin"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni görüşmeler, ekranınızın alt köşesinde simge olarak görünür. Bunları dokunarak genişletebilir veya sürükleyerek kapatabilirsiniz."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Baloncukları istediğiniz zaman kontrol edin"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Buraya dokunarak baloncuk olarak gösterilecek uygulama ve görüşmeleri yönetin"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Bu uygulamanın en boy oranını Ayarlar\'dan değiştirin"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"En boy oranını değiştir"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 78df129d8f50..753fe29ebd41 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"розгорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"згорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зрозуміло"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Немає нещодавніх спливаючих чатів"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Тут з\'являтимуться нещодавні й закриті спливаючі чати"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Спливаючий чат"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Нові розмови відображаються у вигляді значків у нижньому куті екрана. Торкніться, щоб розгорнути їх, або перетягніть, щоб закрити."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Контроль спливаючих чатів"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Натисніть тут, щоб вибрати, для яких додатків і розмов дозволити спливаючі чати"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змінити формат для цього додатка в налаштуваннях"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змінити формат"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index ca1642488768..fb0137658560 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نیچے بائیں جانب لے جائیں"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نیچے دائیں جانب لے جائیں"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو پھیلائیں"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو سکیڑیں"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"سمجھ آ گئی"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"بلبلے کے ذریعے چیٹ کریں"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"نئی گفتگوئیں آپ کی اسکرین کے نیچے کونے میں آئیکنز کے طور پر ظاہر ہوتی ہیں۔ انہیں پھیلانے کے لیے تھپتھپائیں یا انہیں برخاست کرنے کے لیے گھسیٹیں۔"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"یہ نظم کرنے کے لیے یہاں تھپتھپائیں کہ کون سی ایپس اور گفتگوئیں بلبلہ سکتی ہیں"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں۔"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ترتیبات میں اس ایپ کی تناسبی شرح کو تبدیل کریں"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تناسبی شرح کو تبدیل کریں"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index c0dc03366cd6..81e63de9767c 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oʻngga surish"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yoyish"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hech qanday bulutcha topilmadi"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Eng oxirgi va yopilgan bulutchali chatlar shu yerda chiqadi"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bulutchalar yordamida suhbatlashish"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yangi suhbatlar ekraningizning pastki burchagida belgilar shaklida koʻrinadi. Ochish uchun bosing yoki yopish uchun torting"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bulutchalardagi bildirishnomalar"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bulutchalarda bildirishnomalar chiqishiga ruxsat beruvchi ilova va suhbatlarni tanlang."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Sozlamalar orqali bu ilovaning tomonlar nisbatini oʻzgartiring"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tomonlar nisbatini oʻzgartirish"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 7d974005dd31..16bbd5eda23e 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"mở rộng <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gọn <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Đã hiểu"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Không có bong bóng trò chuyện nào gần đây"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyện đã đóng và bong bóng trò chuyện gần đây sẽ xuất hiện ở đây"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Trò chuyện bằng bong bóng trò chuyện"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng ở góc dưới màn hình. Hãy nhấn vào các cuộc trò chuyện đó để mở rộng hoặc kéo để bỏ qua."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kiểm soát bong bóng bất cứ lúc nào"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Nhấn vào đây để quản lý việc dùng bong bóng cho các ứng dụng và cuộc trò chuyện"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Nhấn để khởi động lại ứng dụng này để xem tốt hơn."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Nhấn nút khởi động lại ứng dụng này để xem dễ hơn"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Thay đổi tỷ lệ khung hình của ứng dụng này thông qua phần Cài đặt"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Thay đổi tỷ lệ khung hình"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
diff --git a/libs/WindowManager/Shell/res/values-watch/colors.xml b/libs/WindowManager/Shell/res/values-watch/colors.xml
new file mode 100644
index 000000000000..82492bf2af80
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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.
+ */
+-->
+<resources>
+ <color name="splash_window_background_default">@color/splash_screen_bg_dark</color>
+</resources>
+
diff --git a/libs/WindowManager/Shell/res/values-watch/config.xml b/libs/WindowManager/Shell/res/values-watch/config.xml
new file mode 100644
index 000000000000..03736edc4ec6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for watch products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Animation duration when exit starting window: fade out icon -->
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">50</integer>
+
+ <!-- Animation delay when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_delay">50</integer>
+
+ <!-- Animation duration when exit starting window: reveal app -->
+ <integer name="starting_window_app_reveal_anim_duration">200</integer>
+
+ <!-- Default animation type when hiding the starting window. The possible values are:
+ - 0 for radial vanish + slide up
+ - 1 for fade out -->
+ <integer name="starting_window_exit_animation_type">1</integer>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-watch/dimen.xml b/libs/WindowManager/Shell/res/values-watch/dimen.xml
new file mode 100644
index 000000000000..362e72cb4d2d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-watch/dimen.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (48 X 48) / (72 x 72) -->
+ <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
+ <!-- Scaling factor applied to splash icons without provided background i.e. (60 / 48) -->
+ <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.25</item>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index d1f50dba1ce1..c12ec8423858 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下角"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展开“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收起“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭对话泡"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以对话泡形式显示对话"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用对话泡聊天"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新对话会以图标形式显示在屏幕底部的角落中。点按图标即可展开对话,拖动图标即可关闭对话。"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"随时控制对话泡"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"点按此处即可管理哪些应用和对话可以显示对话泡"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"点按即可重启此应用,获得更好的视图体验。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"点按即可重启此应用,获得更好的视觉体验"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"在“设置”中更改此应用的宽高比"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"更改高宽比"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 6f399e51be4d..c9543488688b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -24,7 +24,7 @@
<string name="pip_menu_title" msgid="5393619322111827096">"選單"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"畫中畫選單"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"如果您不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"如果你不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
<string name="pip_play" msgid="3496151081459417097">"播放"</string>
<string name="pip_pause" msgid="690688849510295232">"暫停"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移去左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移去右下角"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"打開<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收埋<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"沒有最近曾使用的小視窗"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近使用和關閉的小視窗會在這裡顯示"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用對話氣泡進行即時通訊"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"畫面底部的角落會顯示新對話圖示。輕按即可展開圖示;拖曳即可關閉。"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"隨時控制對話氣泡"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"輕按這裡即可管理哪些應用程式和對話可以使用對話氣泡"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更此應用程式的長寬比"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更長寬比"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
@@ -88,8 +96,8 @@
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕按兩下即可調整位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳情。"</string>
- <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"要重新啟動改善檢視畫面嗎?"</string>
- <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"您可重新啟動應用程式,讓系統更新檢視畫面;但系統可能不會儲存目前進度及您作出的任何變更"</string>
+ <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"要重新啟動以改善檢視畫面嗎?"</string>
+ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"你可重新啟動應用程式,讓系統更新檢視畫面;但系統可能不會儲存目前進度及你作出的任何變更"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"取消"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"重新啟動"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"不要再顯示"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 4ca49e167118..d25bfd74ca04 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下方"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下方"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展開「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收合「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"我知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近沒有任何對話框"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近的對話框和已關閉的對話框會顯示在這裡"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"透過對話框進行即時通訊"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"畫面底部的角落會顯示新對話圖示。輕觸可展開圖示,拖曳即可關閉。"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"你隨時可以控管對話框的各項設定"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"輕觸這裡即可管理哪些應用程式和對話可顯示對話框"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"請輕觸並重新啟動此應用程式,取得更良好的觀看體驗。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"輕觸此按鈕重新啟動這個應用程式,即可獲得更良好的觀看體驗"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更這個應用程式的顯示比例"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更顯示比例"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 478b5a62c8a5..bd62b65ccdf2 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -66,6 +66,8 @@
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string>
+ <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"nweba <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string>
@@ -76,10 +78,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ngiyezwa"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xoxa usebenzisa amabhamuza"</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Izingxoxo ezintsha zivela njengezithonjana ekhoneni eliphansi lesikrini sakho. Thepha ukuze uzikhulise noma uhudule ukuze uzichithe."</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Lawula amabhamuza noma nini"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Thepha lapha ukuze ulawule ukuthi yimaphi ama-app kanye nezingxoxo ezingenza amabhamuza"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Shintsha ukubukeka kwesilinganiselo kwe-app kuMasethingi"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Shintsha ukubukeka kwesilinganiselo"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b2ec98bc1b15..9bfd1b44dcca 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,9 +68,10 @@
<color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color>
<color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
<color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
- <color name="desktop_mode_caption_menu_text_color">#191C1D</color>
- <color name="desktop_mode_caption_menu_buttons_color_inactive">#191C1D</color>
- <color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color>
<color name="desktop_mode_resize_veil_light">#EFF1F2</color>
<color name="desktop_mode_resize_veil_dark">#1C1C17</color>
+ <color name="desktop_mode_maximize_menu_button">#DDDACD</color>
+ <color name="desktop_mode_maximize_menu_button_outline">#797869</color>
+ <color name="desktop_mode_maximize_menu_button_outline_on_hover">#606219</color>
+ <color name="desktop_mode_maximize_menu_button_on_hover">#E7E790</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index a3916b71592b..97a9d4874455 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -83,6 +83,11 @@
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">266</integer>
+ <!-- Default animation type when hiding the starting window. The possible values are:
+ - 0 for radial vanish + slide up
+ - 1 for fade out -->
+ <integer name="starting_window_exit_animation_type">0</integer>
+
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 99526de56e4e..7faf3803b11a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -401,6 +401,30 @@
<!-- Height of button (32dp) + 2 * margin (5dp each). -->
<dimen name="freeform_decor_caption_height">42dp</dimen>
+ <!-- Height of desktop mode caption for freeform tasks. -->
+ <dimen name="desktop_mode_freeform_decor_caption_height">42dp</dimen>
+
+ <!-- Height of desktop mode caption for fullscreen tasks. -->
+ <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
+
+ <!-- The width of the maximize menu in desktop mode. -->
+ <dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
+
+ <!-- The height of the maximize menu in desktop mode. -->
+ <dimen name="desktop_mode_maximize_menu_height">112dp</dimen>
+
+ <!-- The larger of the two corner radii of the maximize menu buttons. -->
+ <dimen name="desktop_mode_maximize_menu_buttons_large_corner_radius">4dp</dimen>
+
+ <!-- The smaller of the two corner radii of the maximize menu buttons. -->
+ <dimen name="desktop_mode_maximize_menu_buttons_small_corner_radius">2dp</dimen>
+
+ <!-- The corner radius of the maximize menu. -->
+ <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
+
+ <!-- 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>
@@ -410,9 +434,12 @@
<!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
- <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
+ <!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. -->
<dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen>
+ <!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. -->
+ <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">104dp</dimen>
+
<!-- The top margin of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
@@ -439,4 +466,9 @@
<!-- The height of the area at the top of the screen where a freeform task will transition to
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
+
+ <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
+ <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
+ <!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
+ <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.2</item>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index 8831b610f44a..bc59a235517d 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -42,4 +42,6 @@
<item type="id" name="action_move_top_right"/>
<item type="id" name="action_move_bottom_left"/>
<item type="id" name="action_move_bottom_right"/>
+
+ <item type="id" name="dismiss_view"/>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 00c63d70d3a0..b556150e2ab9 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -142,6 +142,10 @@
<string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
<!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
<string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
+ <!-- Accessibility announcement when the stack of bubbles expands. [CHAR LIMIT=NONE]-->
+ <string name="bubble_accessibility_announce_expand">expand <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string>
+ <!-- Accessibility announcement when the stack of bubbles collapses. [CHAR LIMIT=NONE]-->
+ <string name="bubble_accessibility_announce_collapse">collapse <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string>
<!-- Label for the button that takes the user to the notification settings for the given app. -->
<string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
@@ -163,6 +167,10 @@
<!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
<string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string>
+ <!-- Title text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
+ <string name="bubble_bar_education_stack_title">Chat using bubbles</string>
+ <!-- Descriptive text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] -->
+ <string name="bubble_bar_education_stack_text">New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them.</string>
<!-- Title text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]-->
<string name="bubble_bar_education_manage_title">Control bubbles anytime</string>
<!-- Descriptive text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]-->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index d902fd49ba60..468cfd5260cc 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- Theme used for the activity that shows when the system forced an app to be resizable -->
<style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowBackground">@drawable/forced_resizable_background</item>
@@ -37,7 +38,7 @@
<item name="android:padding">16dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textFontWeight">500</item>
- <item name="android:textColor">@color/desktop_mode_caption_menu_text_color</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:background">?android:selectableItemBackground</item>
</style>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
index 9bf3b80d262e..42dc19ce838a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -52,12 +52,13 @@ public class BackAnimationBackground {
/**
* Ensures the back animation background color layer is present.
+ *
* @param startRect The start bounds of the closing target.
* @param color The background color.
* @param transaction The animation transaction.
*/
- void ensureBackground(Rect startRect, int color,
- @NonNull SurfaceControl.Transaction transaction) {
+ public void ensureBackground(
+ Rect startRect, int color, @NonNull SurfaceControl.Transaction transaction) {
if (mBackgroundSurface != null) {
return;
}
@@ -81,7 +82,12 @@ public class BackAnimationBackground {
mIsRequestingStatusBarAppearance = false;
}
- void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
+ /**
+ * Remove the back animation background.
+ *
+ * @param transaction The animation transaction.
+ */
+ public void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
if (mBackgroundSurface == null) {
return;
}
@@ -93,11 +99,21 @@ public class BackAnimationBackground {
mIsRequestingStatusBarAppearance = false;
}
+ /**
+ * Attach a {@link StatusBarCustomizer} instance to allow status bar animate with back progress.
+ *
+ * @param customizer The {@link StatusBarCustomizer} to be used.
+ */
void setStatusBarCustomizer(StatusBarCustomizer customizer) {
mCustomizer = customizer;
}
- void onBackProgressed(float progress) {
+ /**
+ * Update back animation background with for the progress.
+ *
+ * @param progress Progress value from {@link android.window.BackProgressAnimator}
+ */
+ public void onBackProgressed(float progress) {
if (mCustomizer == null || mStartBounds.isEmpty()) {
return;
}
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 bb543f24a8ea..3790f04b56eb 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
@@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.content.ContentResolver;
@@ -43,7 +44,6 @@ import android.provider.Settings.Global;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
-import android.util.SparseArray;
import android.view.IRemoteAnimationRunner;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -70,6 +70,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -113,7 +114,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private boolean mShouldStartOnNextMoveEvent = false;
/** @see #setTriggerBack(boolean) */
private boolean mTriggerBack;
- private FlingAnimationUtils mFlingAnimationUtils;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+
+ /** Registry for the back animations */
+ private final ShellBackAnimationRegistry mShellBackAnimationRegistry;
@Nullable
private BackNavigationInfo mBackNavigationInfo;
@@ -135,13 +140,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final TouchTracker mTouchTracker = new TouchTracker();
- private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
@Nullable
private IOnBackInvokedCallback mActiveCallback;
- private CrossActivityAnimation mDefaultActivityAnimation;
- private CustomizeActivityAnimation mCustomizeActivityAnimation;
-
@VisibleForTesting
final RemoteCallback mNavigationObserver = new RemoteCallback(
new RemoteCallback.OnResultListener() {
@@ -169,10 +170,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context,
- @NonNull BackAnimationBackground backAnimationBackground) {
- this(shellInit, shellController, shellExecutor, backgroundHandler,
- ActivityTaskManager.getService(), context, context.getContentResolver(),
- backAnimationBackground);
+ @NonNull BackAnimationBackground backAnimationBackground,
+ ShellBackAnimationRegistry shellBackAnimationRegistry) {
+ this(
+ shellInit,
+ shellController,
+ shellExecutor,
+ backgroundHandler,
+ ActivityTaskManager.getService(),
+ context,
+ context.getContentResolver(),
+ backAnimationBackground,
+ shellBackAnimationRegistry);
}
@VisibleForTesting
@@ -182,8 +191,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull IActivityTaskManager activityTaskManager,
- Context context, ContentResolver contentResolver,
- @NonNull BackAnimationBackground backAnimationBackground) {
+ Context context,
+ ContentResolver contentResolver,
+ @NonNull BackAnimationBackground backAnimationBackground,
+ ShellBackAnimationRegistry shellBackAnimationRegistry) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -197,11 +208,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
.setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
.setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
.build();
- }
-
- @VisibleForTesting
- void setEnableUAnimation(boolean enable) {
- IS_U_ANIMATION_ENABLED = enable;
+ mShellBackAnimationRegistry = shellBackAnimationRegistry;
}
private void onInit() {
@@ -209,26 +216,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
createAdapter();
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
this::createExternalInterface, this);
-
- initBackAnimationRunners();
- }
-
- private void initBackAnimationRunners() {
- if (!IS_U_ANIMATION_ENABLED) {
- return;
- }
-
- final CrossTaskBackAnimation crossTaskAnimation =
- new CrossTaskBackAnimation(mContext, mAnimationBackground);
- mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_TASK,
- crossTaskAnimation.mBackAnimationRunner);
- mDefaultActivityAnimation =
- new CrossActivityAnimation(mContext, mAnimationBackground);
- mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- mDefaultActivityAnimation.mBackAnimationRunner);
- mCustomizeActivityAnimation =
- new CustomizeActivityAnimation(mContext, mAnimationBackground);
- // TODO (236760237): register dialog close animation when it's completed.
}
private void setupAnimationDeveloperSettingsObserver(
@@ -359,11 +346,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
void registerAnimation(@BackNavigationInfo.BackTargetType int type,
@NonNull BackAnimationRunner runner) {
- mAnimationDefinition.set(type, runner);
+ mShellBackAnimationRegistry.registerAnimation(type, runner);
}
void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
- mAnimationDefinition.remove(type);
+ mShellBackAnimationRegistry.unregisterAnimation(type);
}
/**
@@ -434,9 +421,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final int backType = backNavigationInfo.getType();
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
if (shouldDispatchToAnimator) {
- if (mAnimationDefinition.contains(backType)) {
- mAnimationDefinition.get(backType).startGesture();
- } else {
+ if (!mShellBackAnimationRegistry.startGesture(backType)) {
mActiveCallback = null;
}
} else {
@@ -459,6 +444,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
sendBackEvent(KeyEvent.ACTION_UP);
}
+ @SuppressLint("MissingPermission")
private void sendBackEvent(int action) {
final long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */,
@@ -671,21 +657,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
final int backType = mBackNavigationInfo.getType();
- final BackAnimationRunner runner = mAnimationDefinition.get(backType);
// Simply trigger and finish back navigation when no animator defined.
- if (!shouldDispatchToAnimator() || runner == null) {
+ if (!shouldDispatchToAnimator()
+ || mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) {
invokeOrCancelBack();
return;
- }
- if (runner.isWaitingAnimation()) {
+ } else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
// Supposed it is in post commit animation state, and start the timeout to watch
// if the animation is ready.
mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
return;
- } else if (runner.isAnimationCancelled()) {
- invokeOrCancelBack();
- return;
}
startPostCommitAnimation();
}
@@ -737,12 +719,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShouldStartOnNextMoveEvent = false;
mTouchTracker.reset();
mActiveCallback = null;
- // reset to default
- if (mDefaultActivityAnimation != null
- && mAnimationDefinition.contains(BackNavigationInfo.TYPE_CROSS_ACTIVITY)) {
- mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- mDefaultActivityAnimation.mBackAnimationRunner);
- }
+ mShellBackAnimationRegistry.resetDefaultCrossActivity();
if (mBackNavigationInfo != null) {
mBackNavigationInfo.onBackNavigationFinished(mTriggerBack);
mBackNavigationInfo = null;
@@ -750,86 +727,88 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTriggerBack = false;
}
- private BackAnimationRunner getAnimationRunnerAndInit() {
- int type = mBackNavigationInfo.getType();
- // Initiate customized cross-activity animation, or fall back to cross activity animation
- if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) {
- final BackNavigationInfo.CustomAnimationInfo animationInfo =
- mBackNavigationInfo.getCustomAnimationInfo();
- if (animationInfo != null && mCustomizeActivityAnimation != null
- && mCustomizeActivityAnimation.prepareNextAnimation(animationInfo)) {
- mAnimationDefinition.get(type).resetWaitingAnimation();
- mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- mCustomizeActivityAnimation.mBackAnimationRunner);
- }
- }
- return mAnimationDefinition.get(type);
- }
private void createAdapter() {
- IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
- @Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IBackAnimationFinishedCallback finishedCallback) {
- mShellExecutor.execute(() -> {
- if (mBackNavigationInfo == null) {
- Log.e(TAG, "Lack of navigation info to start animation.");
- return;
+ IBackAnimationRunner runner =
+ new IBackAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IBackAnimationFinishedCallback finishedCallback) {
+ mShellExecutor.execute(
+ () -> {
+ if (mBackNavigationInfo == null) {
+ Log.e(TAG, "Lack of navigation info to start animation.");
+ return;
+ }
+ final BackAnimationRunner runner =
+ mShellBackAnimationRegistry.getAnimationRunnerAndInit(
+ mBackNavigationInfo);
+ if (runner == null) {
+ if (finishedCallback != null) {
+ try {
+ finishedCallback.onAnimationFinished(false);
+ } catch (RemoteException e) {
+ Log.w(
+ TAG,
+ "Failed call IBackNaviAnimationController",
+ e);
+ }
+ }
+ return;
+ }
+ mActiveCallback = runner.getCallback();
+ mBackAnimationFinishedCallback = finishedCallback;
+
+ ProtoLog.d(
+ WM_SHELL_BACK_PREVIEW,
+ "BackAnimationController: startAnimation()");
+ runner.startAnimation(
+ apps,
+ wallpapers,
+ nonApps,
+ () ->
+ mShellExecutor.execute(
+ BackAnimationController.this
+ ::onBackAnimationFinished));
+
+ if (apps.length >= 1) {
+ dispatchOnBackStarted(
+ mActiveCallback,
+ mTouchTracker.createStartEvent(apps[0]));
+ }
+
+ // Dispatch the first progress after animation start for
+ // smoothing the initial animation, instead of waiting for next
+ // onMove.
+ final BackMotionEvent backFinish =
+ mTouchTracker.createProgressEvent();
+ dispatchOnBackProgressed(mActiveCallback, backFinish);
+ if (!mBackGestureStarted) {
+ // if the down -> up gesture happened before animation
+ // start, we have to trigger the uninterruptible transition
+ // to finish the back animation.
+ startPostCommitAnimation();
+ }
+ });
}
- final int type = mBackNavigationInfo.getType();
- final BackAnimationRunner runner = getAnimationRunnerAndInit();
- if (runner == null) {
- Log.e(TAG, "Animation didn't be defined for type "
- + BackNavigationInfo.typeToString(type));
- if (finishedCallback != null) {
- try {
- finishedCallback.onAnimationFinished(false);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call IBackNaviAnimationController", e);
- }
- }
- return;
- }
- mActiveCallback = runner.getCallback();
- mBackAnimationFinishedCallback = finishedCallback;
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()");
- runner.startAnimation(apps, wallpapers, nonApps, () -> mShellExecutor.execute(
- BackAnimationController.this::onBackAnimationFinished));
-
- if (apps.length >= 1) {
- dispatchOnBackStarted(
- mActiveCallback, mTouchTracker.createStartEvent(apps[0]));
+ @Override
+ public void onAnimationCancelled() {
+ mShellExecutor.execute(
+ () -> {
+ if (!mShellBackAnimationRegistry.cancel(
+ mBackNavigationInfo.getType())) {
+ return;
+ }
+ if (!mBackGestureStarted) {
+ invokeOrCancelBack();
+ }
+ });
}
-
- // Dispatch the first progress after animation start for smoothing the initial
- // animation, instead of waiting for next onMove.
- final BackMotionEvent backFinish = mTouchTracker.createProgressEvent();
- dispatchOnBackProgressed(mActiveCallback, backFinish);
- if (!mBackGestureStarted) {
- // if the down -> up gesture happened before animation start, we have to
- // trigger the uninterruptible transition to finish the back animation.
- startPostCommitAnimation();
- }
- });
- }
-
- @Override
- public void onAnimationCancelled() {
- mShellExecutor.execute(() -> {
- final BackAnimationRunner runner = mAnimationDefinition.get(
- mBackNavigationInfo.getType());
- if (runner == null) {
- return;
- }
- runner.cancelAnimation();
- if (!mBackGestureStarted) {
- invokeOrCancelBack();
- }
- });
- }
- };
+ };
mBackAnimationAdapter = new BackAnimationAdapter(runner);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index 913239f74bf2..431df212f099 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -32,7 +32,7 @@ import android.window.IOnBackInvokedCallback;
* before it received IBackAnimationRunner#onAnimationStart, so the controller could continue
* trigger the real back behavior.
*/
-class BackAnimationRunner {
+public class BackAnimationRunner {
private static final String TAG = "ShellBackPreview";
private final IOnBackInvokedCallback mCallback;
@@ -44,8 +44,8 @@ class BackAnimationRunner {
/** True when the back animation is cancelled */
private boolean mAnimationCancelled;
- BackAnimationRunner(@NonNull IOnBackInvokedCallback callback,
- @NonNull IRemoteAnimationRunner runner) {
+ public BackAnimationRunner(
+ @NonNull IOnBackInvokedCallback callback, @NonNull IRemoteAnimationRunner runner) {
mCallback = callback;
mRunner = runner;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index 74a243d34642..114486e848f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -51,9 +51,11 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import javax.inject.Inject;
+
/** Class that defines cross-activity animation. */
@ShellMainThread
-class CrossActivityAnimation {
+public class CrossActivityAnimation extends ShellBackAnimation {
/**
* Minimum scale of the entering/closing window.
*/
@@ -106,6 +108,7 @@ class CrossActivityAnimation {
private final SpringAnimation mLeavingProgressSpring;
// Max window x-shift in pixels.
private final float mWindowXShift;
+ private final BackAnimationRunner mBackAnimationRunner;
private float mEnteringProgress = 0f;
private float mLeavingProgress = 0f;
@@ -126,11 +129,11 @@ class CrossActivityAnimation {
private IRemoteAnimationFinishedCallback mFinishCallback;
private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
- final BackAnimationRunner mBackAnimationRunner;
private final BackAnimationBackground mBackground;
- CrossActivityAnimation(Context context, BackAnimationBackground background) {
+ @Inject
+ public CrossActivityAnimation(Context context, BackAnimationBackground background) {
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
mBackground = background;
@@ -357,6 +360,11 @@ class CrossActivityAnimation {
mTransaction.apply();
}
+ @Override
+ public BackAnimationRunner getRunner() {
+ return mBackAnimationRunner;
+ }
+
private final class Callback extends IOnBackInvokedCallback.Default {
@Override
public void onBackStarted(BackMotionEvent backEvent) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index a7dd27a0784f..209d8533ee7d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -47,21 +47,23 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import javax.inject.Inject;
+
/**
* Controls the animation of swiping back and returning to another task.
*
- * This is a two part animation. The first part is an animation that tracks gesture location to
- * scale and move the closing and entering app windows.
- * Once the gesture is committed, the second part remains the closing window in place.
- * The entering window plays the rest of app opening transition to enter full screen.
+ * <p>This is a two part animation. The first part is an animation that tracks gesture location to
+ * scale and move the closing and entering app windows. Once the gesture is committed, the second
+ * part remains the closing window in place. The entering window plays the rest of app opening
+ * transition to enter full screen.
*
- * This animation is used only for apps that enable back dispatching via
- * {@link android.window.OnBackInvokedDispatcher}. The controller registers
- * an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
- * navigation to launcher starts.
+ * <p>This animation is used only for apps that enable back dispatching via {@link
+ * android.window.OnBackInvokedDispatcher}. The controller registers an {@link
+ * IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back navigation to
+ * launcher starts.
*/
@ShellMainThread
-class CrossTaskBackAnimation {
+public class CrossTaskBackAnimation extends ShellBackAnimation {
private static final int BACKGROUNDCOLOR = 0x43433A;
/**
@@ -104,28 +106,41 @@ class CrossTaskBackAnimation {
private final float[] mTmpFloat9 = new float[9];
private final float[] mTmpTranslate = {0, 0, 0};
-
+ private final BackAnimationRunner mBackAnimationRunner;
+ private final BackAnimationBackground mBackground;
private RemoteAnimationTarget mEnteringTarget;
private RemoteAnimationTarget mClosingTarget;
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
-
private boolean mBackInProgress = false;
-
private boolean mIsRightEdge;
private float mProgress = 0;
private PointF mTouchPos = new PointF();
private IRemoteAnimationFinishedCallback mFinishCallback;
private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
- final BackAnimationRunner mBackAnimationRunner;
- private final BackAnimationBackground mBackground;
-
- CrossTaskBackAnimation(Context context, BackAnimationBackground background) {
+ @Inject
+ public CrossTaskBackAnimation(Context context, BackAnimationBackground background) {
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
mBackground = background;
}
+ private static void computeScaleTransformMatrix(float scale, float[] matrix) {
+ matrix[0] = scale;
+ matrix[1] = 0;
+ matrix[2] = 0;
+ matrix[3] = 0;
+ matrix[4] = scale;
+ matrix[5] = 0;
+ matrix[6] = 0;
+ matrix[7] = 0;
+ matrix[8] = scale;
+ }
+
+ private static float mapRange(float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
private float getInterpolatedProgress(float backProgress) {
return 1 - (1 - backProgress) * (1 - backProgress) * (1 - backProgress);
}
@@ -233,18 +248,6 @@ class CrossTaskBackAnimation {
mTransaction.setColorTransform(leash, mTmpFloat9, mTmpTranslate);
}
- static void computeScaleTransformMatrix(float scale, float[] matrix) {
- matrix[0] = scale;
- matrix[1] = 0;
- matrix[2] = 0;
- matrix[3] = 0;
- matrix[4] = scale;
- matrix[5] = 0;
- matrix[6] = 0;
- matrix[7] = 0;
- matrix[8] = scale;
- }
-
private void finishAnimation() {
if (mEnteringTarget != null) {
mEnteringTarget.leash.release();
@@ -314,11 +317,12 @@ class CrossTaskBackAnimation {
valueAnimator.start();
}
- private static float mapRange(float value, float min, float max) {
- return min + (value * (max - min));
+ @Override
+ public BackAnimationRunner getRunner() {
+ return mBackAnimationRunner;
}
- private final class Callback extends IOnBackInvokedCallback.Default {
+ private final class Callback extends IOnBackInvokedCallback.Default {
@Override
public void onBackStarted(BackMotionEvent backEvent) {
mProgressAnimator.onBackStarted(backEvent,
@@ -340,7 +344,7 @@ class CrossTaskBackAnimation {
mProgressAnimator.reset();
onGestureCommitted();
}
- };
+ }
private final class Runner extends IRemoteAnimationRunner.Default {
@Override
@@ -360,5 +364,5 @@ class CrossTaskBackAnimation {
startBackAnimation();
mFinishCallback = finishedCallback;
}
- };
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
index 2d6ec7547187..aca638c1a5cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
@@ -55,13 +55,13 @@ import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.annotations.ShellMainThread;
-/**
- * Class that handle customized close activity transition animation.
- */
+import javax.inject.Inject;
+
+/** Class that handle customized close activity transition animation. */
@ShellMainThread
-class CustomizeActivityAnimation {
+public class CustomizeActivityAnimation extends ShellBackAnimation {
private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
- final BackAnimationRunner mBackAnimationRunner;
+ private final BackAnimationRunner mBackAnimationRunner;
private final float mCornerRadius;
private final SurfaceControl.Transaction mTransaction;
private final BackAnimationBackground mBackground;
@@ -88,7 +88,8 @@ class CustomizeActivityAnimation {
private final Choreographer mChoreographer;
- CustomizeActivityAnimation(Context context, BackAnimationBackground background) {
+ @Inject
+ public CustomizeActivityAnimation(Context context, BackAnimationBackground background) {
this(context, background, new SurfaceControl.Transaction(), null);
}
@@ -258,10 +259,12 @@ class CustomizeActivityAnimation {
valueAnimator.start();
}
- /**
- * Load customize animation before animation start.
- */
- boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ /** Load customize animation before animation start. */
+ @Override
+ public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ if (animationInfo == null) {
+ return false;
+ }
final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
if (result != null) {
mCloseAnimation = result.mCloseAnimation;
@@ -272,6 +275,11 @@ class CustomizeActivityAnimation {
return false;
}
+ @Override
+ public BackAnimationRunner getRunner() {
+ return mBackAnimationRunner;
+ }
+
private final class Callback extends IOnBackInvokedCallback.Default {
@Override
public void onBackStarted(BackMotionEvent backEvent) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java
new file mode 100644
index 000000000000..312e88db863e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import android.window.BackNavigationInfo;
+
+import javax.inject.Qualifier;
+
+/** Base class for all back animations. */
+public abstract class ShellBackAnimation {
+ @Qualifier
+ public @interface CrossActivity {}
+
+ @Qualifier
+ public @interface CrossTask {}
+
+ @Qualifier
+ public @interface CustomizeActivity {}
+
+ @Qualifier
+ public @interface ReturnToHome {}
+
+ /** Retrieve the {@link BackAnimationRunner} associated with this animation. */
+ public abstract BackAnimationRunner getRunner();
+
+ /**
+ * Prepare the next animation with customized animation.
+ *
+ * @return true if this type of back animation should override the default.
+ */
+ public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ return false;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
new file mode 100644
index 000000000000..62b18f342995
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.util.SparseArray;
+import android.window.BackNavigationInfo;
+
+/** Registry for all types of default back animations */
+public class ShellBackAnimationRegistry {
+ private static final String TAG = "ShellBackPreview";
+
+ private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
+ private final ShellBackAnimation mDefaultCrossActivityAnimation;
+ private final ShellBackAnimation mCustomizeActivityAnimation;
+
+ public ShellBackAnimationRegistry(
+ @ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation,
+ @ShellBackAnimation.CrossTask @Nullable ShellBackAnimation crossTaskAnimation,
+ @ShellBackAnimation.CustomizeActivity @Nullable
+ ShellBackAnimation customizeActivityAnimation,
+ @ShellBackAnimation.ReturnToHome @Nullable
+ ShellBackAnimation defaultBackToHomeAnimation) {
+ if (crossActivityAnimation != null) {
+ mAnimationDefinition.set(
+ BackNavigationInfo.TYPE_CROSS_TASK, crossTaskAnimation.getRunner());
+ }
+ if (crossActivityAnimation != null) {
+ mAnimationDefinition.set(
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY, crossActivityAnimation.getRunner());
+ }
+ if (defaultBackToHomeAnimation != null) {
+ mAnimationDefinition.set(
+ BackNavigationInfo.TYPE_RETURN_TO_HOME, defaultBackToHomeAnimation.getRunner());
+ }
+
+ mDefaultCrossActivityAnimation = crossActivityAnimation;
+ mCustomizeActivityAnimation = customizeActivityAnimation;
+
+ // TODO(b/236760237): register dialog close animation when it's completed.
+ }
+
+ void registerAnimation(
+ @BackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner) {
+ mAnimationDefinition.set(type, runner);
+ }
+
+ void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
+ mAnimationDefinition.remove(type);
+ }
+
+ /**
+ * Start the {@link BackAnimationRunner} associated with a back target type.
+ *
+ * @param type back target type
+ * @return true if the animation is started, false if animation is not found for that type.
+ */
+ boolean startGesture(@BackNavigationInfo.BackTargetType int type) {
+ BackAnimationRunner runner = mAnimationDefinition.get(type);
+ if (runner == null) {
+ return false;
+ }
+ runner.startGesture();
+ return true;
+ }
+
+ /**
+ * Cancel the {@link BackAnimationRunner} associated with a back target type.
+ *
+ * @param type back target type
+ * @return true if the animation is started, false if animation is not found for that type.
+ */
+ boolean cancel(@BackNavigationInfo.BackTargetType int type) {
+ BackAnimationRunner runner = mAnimationDefinition.get(type);
+ if (runner == null) {
+ return false;
+ }
+ runner.cancelAnimation();
+ return true;
+ }
+
+ boolean isAnimationCancelledOrNull(@BackNavigationInfo.BackTargetType int type) {
+ BackAnimationRunner runner = mAnimationDefinition.get(type);
+ if (runner == null) {
+ return true;
+ }
+ return runner.isAnimationCancelled();
+ }
+
+ boolean isWaitingAnimation(@BackNavigationInfo.BackTargetType int type) {
+ BackAnimationRunner runner = mAnimationDefinition.get(type);
+ if (runner == null) {
+ return false;
+ }
+ return runner.isWaitingAnimation();
+ }
+
+ void resetDefaultCrossActivity() {
+ if (mDefaultCrossActivityAnimation == null
+ || !mAnimationDefinition.contains(BackNavigationInfo.TYPE_CROSS_ACTIVITY)) {
+ return;
+ }
+ mAnimationDefinition.set(
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY, mDefaultCrossActivityAnimation.getRunner());
+ }
+
+ BackAnimationRunner getAnimationRunnerAndInit(BackNavigationInfo backNavigationInfo) {
+ int type = backNavigationInfo.getType();
+ // Initiate customized cross-activity animation, or fall back to cross activity animation
+ if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) {
+ if (mCustomizeActivityAnimation != null
+ && mCustomizeActivityAnimation.prepareNextAnimation(
+ backNavigationInfo.getCustomAnimationInfo())) {
+ mAnimationDefinition.get(type).resetWaitingAnimation();
+ mAnimationDefinition.set(
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY,
+ mCustomizeActivityAnimation.getRunner());
+ }
+ }
+ BackAnimationRunner runner = mAnimationDefinition.get(type);
+ if (runner == null) {
+ Log.e(
+ TAG,
+ "Animation didn't be defined for type "
+ + BackNavigationInfo.typeToString(type));
+ }
+ return runner;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING
index 837d5ff3b073..f02559f36169 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING
@@ -12,19 +12,19 @@
]
},
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceBackNavigation",
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
- "include-filter": "android.server.wm.BackGestureInvokedTest"
+ "include-filter": "android.server.wm.backnavigation.BackGestureInvokedTest"
},
{
- "include-filter": "android.server.wm.BackNavigationTests"
+ "include-filter": "android.server.wm.backnavigation.BackNavigationTests"
},
{
- "include-filter": "android.server.wm.OnBackInvokedCallbackGestureTest"
+ "include-filter": "android.server.wm.backnavigation.OnBackInvokedCallbackGestureTest"
}
]
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 9a2b81243861..85ea8097a2c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -973,9 +973,9 @@ public class Bubble implements BubbleViewProvider {
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
pw.print(" isDismissable: "); pw.println(mIsDismissable);
- pw.println(" bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null));
+ pw.println(" bubbleMetadataFlagListener null?: " + (mBubbleMetadataFlagListener == null));
if (mExpandedView != null) {
- mExpandedView.dump(pw);
+ mExpandedView.dump(pw, " ");
}
}
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 8400ddec0af5..f259902e9565 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.bubbles;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -57,6 +56,7 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Binder;
@@ -115,6 +115,7 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -143,6 +144,8 @@ public class BubbleController implements ConfigurationChangeListener,
// Should match with PhoneWindowManager
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
+ private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+ private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
/**
* Common interface to send updates to bubble views.
@@ -182,6 +185,7 @@ public class BubbleController implements ConfigurationChangeListener,
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
private final TaskViewTransitions mTaskViewTransitions;
+ private final Transitions mTransitions;
private final SyncTransactionQueue mSyncQueue;
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
@@ -282,6 +286,7 @@ public class BubbleController implements ConfigurationChangeListener,
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
+ Transitions transitions,
SyncTransactionQueue syncQueue,
IWindowManager wmService,
BubbleProperties bubbleProperties) {
@@ -317,6 +322,7 @@ public class BubbleController implements ConfigurationChangeListener,
com.android.internal.R.dimen.importance_ring_stroke_width));
mDisplayController = displayController;
mTaskViewTransitions = taskViewTransitions;
+ mTransitions = transitions;
mOneHandedOptional = oneHandedOptional;
mDragAndDropController = dragAndDropController;
mSyncQueue = syncQueue;
@@ -416,23 +422,9 @@ public class BubbleController implements ConfigurationChangeListener,
}
}, mMainHandler);
- mTaskStackListener.addListener(new TaskStackListenerCallback() {
- @Override
- public void onTaskMovedToFront(int taskId) {
- mMainExecutor.execute(() -> {
- int expandedId = INVALID_TASK_ID;
- if (mStackView != null && mStackView.getExpandedBubble() != null
- && isStackExpanded()
- && !mStackView.isExpansionAnimating()
- && !mStackView.isSwitchAnimating()) {
- expandedId = mStackView.getExpandedBubble().getTaskId();
- }
- if (expandedId != INVALID_TASK_ID && expandedId != taskId) {
- mBubbleData.setExpanded(false);
- }
- });
- }
+ mTransitions.registerObserver(new BubblesTransitionObserver(this, mBubbleData));
+ mTaskStackListener.addListener(new TaskStackListenerCallback() {
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
@@ -883,8 +875,10 @@ public class BubbleController implements ConfigurationChangeListener,
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
- if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason))
+ boolean validReasonToCollapse = SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)
+ || SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)
+ || SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason);
+ if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) && validReasonToCollapse)
|| Intent.ACTION_SCREEN_OFF.equals(action)) {
mMainExecutor.execute(() -> collapseStack());
}
@@ -1070,6 +1064,15 @@ public class BubbleController implements ConfigurationChangeListener,
}
}
+ /**
+ * Show bubble bar user education relative to the reference position.
+ * @param position the reference position in Screen coordinates.
+ */
+ public void showUserEducation(Point position) {
+ if (mLayerView == null) return;
+ mLayerView.showUserEducation(position);
+ }
+
@VisibleForTesting
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
@@ -1122,6 +1125,16 @@ public class BubbleController implements ConfigurationChangeListener,
}
/**
+ * Expands the stack if the selected bubble is present. This is currently used when user
+ * education view is clicked to expand the selected bubble.
+ */
+ public void expandStackWithSelectedBubble() {
+ if (mBubbleData.getSelectedBubble() != null) {
+ mBubbleData.setExpanded(true);
+ }
+ }
+
+ /**
* Expands and selects the provided bubble as long as it already exists in the stack or the
* overflow. This is currently used when opening a bubble via clicking on a conversation widget.
*/
@@ -1737,7 +1750,8 @@ public class BubbleController implements ConfigurationChangeListener,
+ " expandedChanged=" + update.expandedChanged
+ " selectionChanged=" + update.selectionChanged
+ " suppressed=" + (update.suppressedBubble != null)
- + " unsuppressed=" + (update.unsuppressedBubble != null));
+ + " unsuppressed=" + (update.unsuppressedBubble != null)
+ + " shouldShowEducation=" + update.shouldShowEducation);
}
ensureBubbleViewsAndWindowCreated();
@@ -1961,6 +1975,15 @@ public class BubbleController implements ConfigurationChangeListener,
}
}
+ /**
+ * Returns whether the stack is animating or not.
+ */
+ public boolean isStackAnimating() {
+ return mStackView != null
+ && (mStackView.isExpansionAnimating()
+ || mStackView.isSwitchAnimating());
+ }
+
@VisibleForTesting
@Nullable
public BubbleStackView getStackView() {
@@ -1989,13 +2012,20 @@ public class BubbleController implements ConfigurationChangeListener,
* Description of current bubble state.
*/
private void dump(PrintWriter pw, String prefix) {
- pw.println("BubbleController state:");
+ pw.print(prefix); pw.println("BubbleController state:");
+ pw.print(prefix); pw.println(" currentUserId= " + mCurrentUserId);
+ pw.print(prefix); pw.println(" isStatusBarShade= " + mIsStatusBarShade);
+ pw.print(prefix); pw.println(" isShowingAsBubbleBar= " + isShowingAsBubbleBar());
+ pw.println();
+
mBubbleData.dump(pw);
pw.println();
+
if (mStackView != null) {
mStackView.dump(pw);
}
pw.println();
+
mImpl.mCachedState.dump(pw);
}
@@ -2146,6 +2176,12 @@ public class BubbleController implements ConfigurationChangeListener,
public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
mMainExecutor.execute(() -> mController.onBubbleDrag(bubbleKey, isBeingDragged));
}
+
+ @Override
+ public void showUserEducation(int positionX, int positionY) {
+ mMainExecutor.execute(() ->
+ mController.showUserEducation(new Point(positionX, positionY)));
+ }
}
private class BubblesImpl implements Bubbles {
@@ -2244,8 +2280,7 @@ public class BubbleController implements ConfigurationChangeListener,
pw.println("mIsStackExpanded: " + mIsStackExpanded);
pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey);
- pw.print("mSuppressedBubbleKeys: ");
- pw.println(mSuppressedBubbleKeys.size());
+ pw.println("mSuppressedBubbleKeys: " + mSuppressedBubbleKeys.size());
for (String key : mSuppressedBubbleKeys) {
pw.println(" suppressing: " + key);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index cc8f50e09fcb..595a4afbfc86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -77,6 +77,7 @@ public class BubbleData {
boolean orderChanged;
boolean suppressedSummaryChanged;
boolean expanded;
+ boolean shouldShowEducation;
@Nullable BubbleViewProvider selectedBubble;
@Nullable Bubble addedBubble;
@Nullable Bubble updatedBubble;
@@ -126,6 +127,7 @@ public class BubbleData {
bubbleBarUpdate.expandedChanged = expandedChanged;
bubbleBarUpdate.expanded = expanded;
+ bubbleBarUpdate.shouldShowEducation = shouldShowEducation;
if (selectionChanged) {
bubbleBarUpdate.selectedBubbleKey = selectedBubble != null
? selectedBubble.getKey()
@@ -165,6 +167,7 @@ public class BubbleData {
*/
BubbleBarUpdate getInitialState() {
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
+ bubbleBarUpdate.shouldShowEducation = shouldShowEducation;
for (int i = 0; i < bubbles.size(); i++) {
bubbleBarUpdate.currentBubbleList.add(bubbles.get(i).asBubbleBarBubble());
}
@@ -187,6 +190,7 @@ public class BubbleData {
private final Context mContext;
private final BubblePositioner mPositioner;
+ private final BubbleEducationController mEducationController;
private final Executor mMainExecutor;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
@@ -233,10 +237,11 @@ public class BubbleData {
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner,
- Executor mainExecutor) {
+ BubbleEducationController educationController, Executor mainExecutor) {
mContext = context;
mLogger = bubbleLogger;
mPositioner = positioner;
+ mEducationController = educationController;
mMainExecutor = mainExecutor;
mOverflow = new BubbleOverflow(context, positioner);
mBubbles = new ArrayList<>();
@@ -447,6 +452,7 @@ public class BubbleData {
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
setSelectedBubbleInternal(bubble);
+
if (!mExpanded) {
setExpandedInternal(true);
}
@@ -877,6 +883,9 @@ public class BubbleData {
private void dispatchPendingChanges() {
if (mListener != null && mStateChange.anythingChanged()) {
+ mStateChange.shouldShowEducation = mSelectedBubble != null
+ && mEducationController.shouldShowStackEducation(mSelectedBubble)
+ && !mExpanded;
mListener.applyUpdate(mStateChange);
}
mStateChange = new Update(mBubbles, mOverflowBubbles);
@@ -1231,29 +1240,30 @@ public class BubbleData {
* Description of current bubble data state.
*/
public void dump(PrintWriter pw) {
- pw.print("selected: ");
+ pw.println("BubbleData state:");
+ pw.print(" selected: ");
pw.println(mSelectedBubble != null
? mSelectedBubble.getKey()
: "null");
- pw.print("expanded: ");
+ pw.print(" expanded: ");
pw.println(mExpanded);
- pw.print("stack bubble count: ");
+ pw.print("Stack bubble count: ");
pw.println(mBubbles.size());
for (Bubble bubble : mBubbles) {
bubble.dump(pw);
}
- pw.print("overflow bubble count: ");
+ pw.print("Overflow bubble count: ");
pw.println(mOverflowBubbles.size());
for (Bubble bubble : mOverflowBubbles) {
bubble.dump(pw);
}
- pw.print("summaryKeys: ");
+ pw.print("SummaryKeys: ");
pw.println(mSuppressedGroupKeys.size());
for (String key : mSuppressedGroupKeys.keySet()) {
- pw.println(" suppressing: " + key);
+ pw.println(" suppressing: " + key);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 76662c47238f..f56b1712c5c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -44,7 +44,7 @@ public class BubbleDebugConfig {
static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false;
static final boolean DEBUG_EXPERIMENTS = true;
static final boolean DEBUG_OVERFLOW = false;
- static final boolean DEBUG_USER_EDUCATION = false;
+ public static final boolean DEBUG_USER_EDUCATION = false;
static final boolean DEBUG_POSITIONER = false;
public static final boolean DEBUG_COLLAPSE_ANIMATOR = false;
public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false;
@@ -77,20 +77,25 @@ public class BubbleDebugConfig {
static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) {
StringBuilder sb = new StringBuilder();
- for (Bubble bubble : bubbles) {
+ for (int i = 0; i < bubbles.size(); i++) {
+ Bubble bubble = bubbles.get(i);
if (bubble == null) {
- sb.append(" <null> !!!!!\n");
+ sb.append(" <null> !!!!!");
} else {
boolean isSelected = (selected != null
- && selected.getKey() != BubbleOverflow.KEY
+ && !BubbleOverflow.KEY.equals(selected.getKey())
&& bubble == selected);
String arrow = isSelected ? "=>" : " ";
- sb.append(String.format("%s Bubble{act=%12d, showInShade=%d, key=%s}\n",
+
+ sb.append(String.format("%s Bubble{act=%12d, showInShade=%d, key=%s}",
arrow,
bubble.getLastActivity(),
(bubble.showInShade() ? 1 : 0),
bubble.getKey()));
}
+ if (i != bubbles.size() - 1) {
+ sb.append("\n");
+ }
}
return sb.toString();
}
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 e6986012dd88..37bcf1ddeac5 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
@@ -1094,9 +1094,9 @@ public class BubbleExpandedView extends LinearLayout {
/**
* Description of current expanded view state.
*/
- public void dump(@NonNull PrintWriter pw) {
- pw.print("BubbleExpandedView");
- pw.print(" taskId: "); pw.println(mTaskId);
- pw.print(" stackView: "); pw.println(mStackView);
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.print(prefix); pw.println("BubbleExpandedView:");
+ pw.print(prefix); pw.print(" taskId: "); pw.println(mTaskId);
+ pw.print(prefix); pw.print(" stackView: "); pw.println(mStackView);
}
}
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 52c9bf8462ec..c124b532b89d 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
@@ -25,6 +25,7 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.animation.Animator;
@@ -131,8 +132,6 @@ public class BubbleStackView extends FrameLayout
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
- private static final float SCRIM_ALPHA = 0.32f;
-
/** Minimum alpha value for scrim when alpha is being changed via drag */
private static final float MIN_SCRIM_ALPHA_FOR_DRAG = 0.2f;
@@ -305,8 +304,7 @@ public class BubbleStackView extends FrameLayout
String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
getBubblesOnScreen(), getExpandedBubble());
- pw.print(" stack visibility : "); pw.println(getVisibility());
- pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen);
+ pw.println(" bubbles on screen: "); pw.println(bubblesOnScreen);
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
@@ -314,7 +312,8 @@ public class BubbleStackView extends FrameLayout
pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha());
pw.print(" expandedContainerMatrix: ");
pw.println(mExpandedViewContainer.getAnimationMatrix());
-
+ pw.print(" stack visibility : "); pw.println(getVisibility());
+ pw.print(" temporarilyInvisible: "); pw.println(mTemporarilyInvisible);
mStackAnimationController.dump(pw);
mExpandedAnimationController.dump(pw);
@@ -779,14 +778,15 @@ public class BubbleStackView extends FrameLayout
private float getScrimAlphaForDrag(float dragAmount) {
// dragAmount should be negative as we allow scroll up only
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- float alphaRange = SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG;
+ float alphaRange = BUBBLE_EXPANDED_SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG;
int dragMax = mExpandedBubble.getExpandedView().getContentHeight();
float dragFraction = dragAmount / dragMax;
- return Math.max(SCRIM_ALPHA - alphaRange * dragFraction, MIN_SCRIM_ALPHA_FOR_DRAG);
+ return Math.max(BUBBLE_EXPANDED_SCRIM_ALPHA - alphaRange * dragFraction,
+ MIN_SCRIM_ALPHA_FOR_DRAG);
}
- return SCRIM_ALPHA;
+ return BUBBLE_EXPANDED_SCRIM_ALPHA;
}
};
@@ -2037,6 +2037,7 @@ public class BubbleStackView extends FrameLayout
});
}
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+ announceExpandForAccessibility(mExpandedBubble, mIsExpanded);
}
/**
@@ -2053,6 +2054,34 @@ public class BubbleStackView extends FrameLayout
}
}
+ private void announceExpandForAccessibility(BubbleViewProvider bubble, boolean expanded) {
+ if (bubble instanceof Bubble) {
+ String contentDescription = getBubbleContentDescription((Bubble) bubble);
+ String message = getResources().getString(
+ expanded
+ ? R.string.bubble_accessibility_announce_expand
+ : R.string.bubble_accessibility_announce_collapse, contentDescription);
+ announceForAccessibility(message);
+ }
+ }
+
+ @NonNull
+ private String getBubbleContentDescription(Bubble bubble) {
+ final String appName = bubble.getAppName();
+ final String title = bubble.getTitle() != null
+ ? bubble.getTitle()
+ : getResources().getString(R.string.notification_bubble_title);
+
+ if (appName == null || title.equals(appName)) {
+ // App bubble title equals the app name, so return only the title to avoid having
+ // content description like: `<app> from <app>`.
+ return title;
+ } else {
+ return getResources().getString(
+ R.string.bubble_content_description_single, title, appName);
+ }
+ }
+
private boolean isGestureNavEnabled() {
return mContext.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode)
@@ -2214,7 +2243,7 @@ public class BubbleStackView extends FrameLayout
if (show) {
mScrim.animate()
.setInterpolator(ALPHA_IN)
- .alpha(SCRIM_ALPHA)
+ .alpha(BUBBLE_EXPANDED_SCRIM_ALPHA)
.setListener(listener)
.start();
} else {
@@ -2943,7 +2972,7 @@ public class BubbleStackView extends FrameLayout
mBubbleController.getSysuiProxy().onManageMenuExpandChanged(show);
mManageMenuScrim.animate()
.setInterpolator(show ? ALPHA_IN : ALPHA_OUT)
- .alpha(show ? SCRIM_ALPHA : 0f)
+ .alpha(show ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0f)
.withEndAction(endAction)
.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 39e3180ffe2a..bb30c5eeebcf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -155,14 +155,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Bitmap rawBadgeBitmap;
// Only populated when showing in taskbar
- BubbleBarExpandedView bubbleBarExpandedView;
+ @Nullable BubbleBarExpandedView bubbleBarExpandedView;
// These are only populated when not showing in taskbar
- BadgedImageView imageView;
- BubbleExpandedView expandedView;
+ @Nullable BadgedImageView imageView;
+ @Nullable BubbleExpandedView expandedView;
int dotColor;
Path dotPath;
- Bubble.FlyoutMessage flyoutMessage;
+ @Nullable Bubble.FlyoutMessage flyoutMessage;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java
new file mode 100644
index 000000000000..9e8a385262e4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
+
+/**
+ * Observer used to identify tasks that are opening or moving to front. If a bubble activity is
+ * currently opened when this happens, we'll collapse the bubbles.
+ */
+public class BubblesTransitionObserver implements Transitions.TransitionObserver {
+
+ private BubbleController mBubbleController;
+ private BubbleData mBubbleData;
+
+ public BubblesTransitionObserver(BubbleController controller,
+ BubbleData bubbleData) {
+ mBubbleController = controller;
+ mBubbleData = bubbleData;
+ }
+
+ @Override
+ public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ // We only care about opens / move to fronts when bubbles are expanded & not animating.
+ if (taskInfo == null
+ || taskInfo.taskId == INVALID_TASK_ID
+ || !TransitionUtil.isOpeningType(change.getMode())
+ || mBubbleController.isStackAnimating()
+ || !mBubbleData.isExpanded()
+ || mBubbleData.getSelectedBubble() == null) {
+ continue;
+ }
+ int expandedId = mBubbleData.getSelectedBubble().getTaskId();
+ // If the task id that's opening is the same as the expanded bubble, skip collapsing
+ // because it is our bubble that is opening.
+ if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
+ mBubbleData.setExpanded(false);
+ }
+ }
+ }
+
+ @Override
+ public void onTransitionStarting(@NonNull IBinder transition) {
+
+ }
+
+ @Override
+ public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
+
+ }
+
+ @Override
+ public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
+
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
index ed3624035757..48692d41016e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -22,6 +22,7 @@ import com.android.wm.shell.common.bubbles.DismissView
fun DismissView.setup() {
setup(DismissView.Config(
+ dismissViewResId = R.id.dismiss_view,
targetSizeResId = R.dimen.dismiss_circle_size,
iconSizeResId = R.dimen.dismiss_target_x_size,
bottomMarginResId = R.dimen.floating_dismiss_bottom_margin,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 4dda0688b790..5776ad109d19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -39,4 +39,6 @@ interface IBubbles {
oneway void onBubbleDrag(in String key, in boolean isBeingDragged) = 7;
+ oneway void showUserEducation(in int positionX, in int positionY) = 8;
+
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 8f11253290ea..e788341df5f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -21,6 +21,7 @@ import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
@@ -36,6 +37,8 @@ import com.android.wm.shell.bubbles.BubbleViewProvider;
import java.util.function.Consumer;
+import kotlin.Unit;
+
/**
* Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window
* manager to display bubbles. However, it is only used when bubbles are being displayed in
@@ -111,7 +114,7 @@ public class BubbleBarLayerView extends FrameLayout
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
if (mExpandedView != null) {
- mEducationViewController.hideManageEducation(/* animated = */ false);
+ mEducationViewController.hideEducation(/* animated = */ false);
removeView(mExpandedView);
mExpandedView = null;
}
@@ -171,7 +174,9 @@ public class BubbleBarLayerView extends FrameLayout
mExpandedView.setListener(new BubbleBarExpandedView.Listener() {
@Override
public void onTaskCreated() {
- mEducationViewController.maybeShowManageEducation(b, mExpandedView);
+ if (mEducationViewController != null && mExpandedView != null) {
+ mEducationViewController.maybeShowManageEducation(b, mExpandedView);
+ }
}
@Override
@@ -190,6 +195,10 @@ public class BubbleBarLayerView extends FrameLayout
addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
}
+ if (mEducationViewController.isEducationVisible()) {
+ mEducationViewController.hideEducation(/* animated = */ true);
+ }
+
mIsExpanded = true;
mBubbleController.getSysuiProxy().onStackExpandChanged(true);
mAnimationHelper.animateExpansion(mExpandedBubble, () -> {
@@ -210,7 +219,7 @@ public class BubbleBarLayerView extends FrameLayout
public void collapse() {
mIsExpanded = false;
final BubbleBarExpandedView viewToRemove = mExpandedView;
- mEducationViewController.hideManageEducation(/* animated = */ true);
+ mEducationViewController.hideEducation(/* animated = */ true);
mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
mBubbleController.getSysuiProxy().onStackExpandChanged(false);
mExpandedView = null;
@@ -218,6 +227,21 @@ public class BubbleBarLayerView extends FrameLayout
showScrim(false);
}
+ /**
+ * Show bubble bar user education relative to the reference position.
+ * @param position the reference position in Screen coordinates.
+ */
+ public void showUserEducation(Point position) {
+ mEducationViewController.showStackEducation(position, /* root = */ this, () -> {
+ // When the user education is clicked hide it and expand the selected bubble
+ mEducationViewController.hideEducation(/* animated = */ true, () -> {
+ mBubbleController.expandStackWithSelectedBubble();
+ return Unit.INSTANCE;
+ });
+ return Unit.INSTANCE;
+ });
+ }
+
/** Sets the function to call to un-bubble the given conversation. */
public void setUnBubbleConversationCallback(
@Nullable Consumer<String> unBubbleConversationCallback) {
@@ -226,8 +250,8 @@ public class BubbleBarLayerView extends FrameLayout
/** Hides the current modal education/menu view, expanded view or collapses the bubble stack */
private void hideMenuOrCollapse() {
- if (mEducationViewController.isManageEducationVisible()) {
- mEducationViewController.hideManageEducation(/* animated = */ true);
+ if (mEducationViewController.isEducationVisible()) {
+ mEducationViewController.hideEducation(/* animated = */ true);
} else if (isExpanded() && mExpandedView != null) {
mExpandedView.hideMenuOrCollapse();
} else {
@@ -275,7 +299,7 @@ public class BubbleBarLayerView extends FrameLayout
*/
private void getTouchableRegion(Region outRegion) {
mTempRect.setEmpty();
- if (mIsExpanded) {
+ if (mIsExpanded || mEducationViewController.isEducationVisible()) {
getBoundsOnScreen(mTempRect);
outRegion.op(mTempRect, Region.Op.UNION);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7b39c6fd4059..ee552ae204b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -15,23 +15,34 @@
*/
package com.android.wm.shell.bubbles.bar
+import android.annotation.LayoutRes
import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.Log
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.core.view.doOnLayout
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
+import com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.common.bubbles.BubblePopupDrawable
import com.android.wm.shell.common.bubbles.BubblePopupView
+import kotlin.math.roundToInt
/** Manages bubble education presentation and animation */
class BubbleEducationViewController(private val context: Context, private val listener: Listener) {
interface Listener {
- fun onManageEducationVisibilityChanged(isVisible: Boolean)
+ fun onEducationVisibilityChanged(isVisible: Boolean)
}
private var rootView: ViewGroup? = null
@@ -45,61 +56,112 @@ class BubbleEducationViewController(private val context: Context, private val li
)
}
+ private val scrimView by lazy {
+ View(context).apply {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ setOnClickListener { hideEducation(animated = true) }
+ }
+ }
+
private val controller by lazy { BubbleEducationController(context) }
/** Whether the education view is visible or being animated */
- val isManageEducationVisible: Boolean
+ val isEducationVisible: Boolean
get() = educationView != null && rootView != null
/**
- * Show manage bubble education if hasn't been shown before
+ * Hide the current education view if visible
*
- * @param bubble the bubble used for the manage education check
- * @param root the view to show manage education in
+ * @param animated whether should hide with animation
*/
- fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) {
- if (!controller.shouldShowManageEducation(bubble)) return
- showManageEducation(root)
+ @JvmOverloads
+ fun hideEducation(animated: Boolean, endActions: () -> Unit = {}) {
+ log { "hideEducation animated: $animated" }
+
+ if (animated) {
+ animateTransition(show = false) {
+ cleanUp()
+ endActions()
+ listener.onEducationVisibilityChanged(isVisible = false)
+ }
+ } else {
+ cleanUp()
+ endActions()
+ listener.onEducationVisibilityChanged(isVisible = false)
+ }
}
/**
- * Hide the manage education view if visible
+ * Show bubble bar stack user education.
*
- * @param animated whether should hide with animation
+ * @param position the reference position for the user education in Screen coordinates.
+ * @param root the view to show user education in.
+ * @param educationClickHandler the on click handler for the user education view
*/
- fun hideManageEducation(animated: Boolean) {
- rootView?.let {
- fun cleanUp() {
- it.removeView(educationView)
- rootView = null
- listener.onManageEducationVisibilityChanged(isVisible = false)
- }
+ fun showStackEducation(position: Point, root: ViewGroup, educationClickHandler: () -> Unit) {
+ hideEducation(animated = false)
+ log { "showStackEducation at: $position" }
- if (animated) {
- animateTransition(show = false, ::cleanUp)
- } else {
- cleanUp()
+ educationView =
+ createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+ setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
+ setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+ updateEducationPosition(view = this, position, root)
+ val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
+ doOnLayout {
+ it.pivotX = it.width - arrowToEdgeOffset
+ it.pivotY = it.height.toFloat()
+ }
+ setOnClickListener { educationClickHandler() }
}
+
+ rootView = root
+ animator = createAnimator()
+
+ root.addView(scrimView)
+ root.addView(educationView)
+ animateTransition(show = true) {
+ controller.hasSeenStackEducation = true
+ listener.onEducationVisibilityChanged(isVisible = true)
}
}
/**
+ * Show manage bubble education if hasn't been shown before
+ *
+ * @param bubble the bubble used for the manage education check
+ * @param root the view to show manage education in
+ */
+ fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) {
+ log { "maybeShowManageEducation bubble: $bubble" }
+ if (!controller.shouldShowManageEducation(bubble)) return
+ showManageEducation(root)
+ }
+
+ /**
* Show manage education with animation
*
* @param root the view to show manage education in
*/
private fun showManageEducation(root: ViewGroup) {
- hideManageEducation(animated = false)
- if (educationView == null) {
- val eduView = createEducationView(root)
- educationView = eduView
- animator = createAnimation(eduView)
- }
- root.addView(educationView)
+ hideEducation(animated = false)
+ log { "showManageEducation" }
+
+ educationView =
+ createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+ pivotY = 0f
+ doOnLayout { it.pivotX = it.width / 2f }
+ setOnClickListener { hideEducation(animated = true) }
+ }
+
rootView = root
+ animator = createAnimator()
+
+ root.addView(scrimView)
+ root.addView(educationView)
animateTransition(show = true) {
controller.hasSeenManageEducation = true
- listener.onManageEducationVisibilityChanged(isVisible = true)
+ listener.onEducationVisibilityChanged(isVisible = true)
}
}
@@ -110,39 +172,75 @@ class BubbleEducationViewController(private val context: Context, private val li
* @param endActions a closure to be called when the animation completes
*/
private fun animateTransition(show: Boolean, endActions: () -> Unit) {
- animator?.let { animator ->
- animator
- .spring(DynamicAnimation.ALPHA, if (show) 1f else 0f)
- .spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN)
- .spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN)
- .withEndActions(endActions)
- .start()
- } ?: endActions()
+ animator
+ ?.spring(DynamicAnimation.ALPHA, if (show) 1f else 0f)
+ ?.spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN)
+ ?.spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN)
+ ?.withEndActions(endActions)
+ ?.start()
+ ?: endActions()
+ }
+
+ /** Remove education view from the root and clean up all relative properties */
+ private fun cleanUp() {
+ log { "cleanUp" }
+ rootView?.removeView(educationView)
+ rootView?.removeView(scrimView)
+ educationView = null
+ rootView = null
+ animator = null
}
- private fun createEducationView(root: ViewGroup): BubblePopupView {
- val view =
- LayoutInflater.from(context).inflate(R.layout.bubble_bar_manage_education, root, false)
- as BubblePopupView
-
- return view.apply {
- setup()
- alpha = 0f
- pivotY = 0f
- scaleX = EDU_SCALE_HIDDEN
- scaleY = EDU_SCALE_HIDDEN
- doOnLayout { it.pivotX = it.width / 2f }
- setOnClickListener { hideManageEducation(animated = true) }
+ /**
+ * Create education view by inflating layout provided.
+ *
+ * @param layout layout resource id to inflate. The root view should be [BubblePopupView]
+ * @param root view group to use as root for inflation, is not attached to root
+ */
+ private fun createEducationView(@LayoutRes layout: Int, root: ViewGroup): BubblePopupView {
+ val view = LayoutInflater.from(context).inflate(layout, root, false) as BubblePopupView
+ view.setup()
+ view.alpha = 0f
+ view.scaleX = EDU_SCALE_HIDDEN
+ view.scaleY = EDU_SCALE_HIDDEN
+ return view
+ }
+
+ /** Create animator for the user education transitions */
+ private fun createAnimator(): PhysicsAnimator<BubblePopupView>? {
+ return educationView?.let {
+ PhysicsAnimator.getInstance(it).apply { setDefaultSpringConfig(springConfig) }
}
}
- private fun createAnimation(view: BubblePopupView): PhysicsAnimator<BubblePopupView> {
- val animator = PhysicsAnimator.getInstance(view)
- animator.setDefaultSpringConfig(springConfig)
- return animator
+ /**
+ * Update user education view position relative to the reference position
+ *
+ * @param view the user education view to layout
+ * @param position the reference position in Screen coordinates
+ * @param root the root view to use for the layout
+ */
+ private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
+ val rootBounds = Rect()
+ // Get root bounds on screen as position is in screen coordinates
+ root.getBoundsOnScreen(rootBounds)
+ // Get the offset to the arrow from the edge of the education view
+ val arrowToEdgeOffset =
+ view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
+ ?: 0
+ // Calculate education view margins
+ val params = view.layoutParams as FrameLayout.LayoutParams
+ params.bottomMargin = rootBounds.bottom - position.y
+ params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ view.layoutParams = params
+ }
+
+ private fun log(msg: () -> String) {
+ if (DEBUG_USER_EDUCATION) Log.d(TAG, msg())
}
companion object {
+ private val TAG = if (TAG_WITH_CLASS_NAME) "BubbleEducationViewController" else TAG_BUBBLES
private const val EDU_SCALE_HIDDEN = 0.5f
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 72702e7c2b88..b828aac39040 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.IDisplayChangeWindowCallback;
import android.view.IDisplayChangeWindowController;
@@ -40,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public class DisplayChangeController {
private static final String TAG = DisplayChangeController.class.getSimpleName();
+ private static final String HANDLE_DISPLAY_CHANGE_TRACE_TAG = "HandleRemoteDisplayChange";
private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
@@ -81,9 +83,15 @@ public class DisplayChangeController {
/** Query all listeners for changes that should happen on display change. */
void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginSection("dispatchOnDisplayChange");
+ }
for (OnDisplayChangingListener c : mDisplayChangeListener) {
c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endSection();
+ }
}
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@@ -94,6 +102,10 @@ public class DisplayChangeController {
callback.continueDisplayChange(t);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to continue handling display change", e);
+ } finally {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.endAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
}
}
@@ -103,6 +115,9 @@ public class DisplayChangeController {
@Override
public void onDisplayChange(int displayId, int fromRotation, int toRotation,
DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.beginAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode());
+ }
mMainExecutor.execute(() -> DisplayChangeController.this
.onDisplayChange(displayId, fromRotation, toRotation,
newDisplayAreaInfo, callback));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
index 81423473171d..fc627a8dcb36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
@@ -35,6 +35,7 @@ public class BubbleBarUpdate implements Parcelable {
public boolean expandedChanged;
public boolean expanded;
+ public boolean shouldShowEducation;
@Nullable
public String selectedBubbleKey;
@Nullable
@@ -61,6 +62,7 @@ public class BubbleBarUpdate implements Parcelable {
public BubbleBarUpdate(Parcel parcel) {
expandedChanged = parcel.readBoolean();
expanded = parcel.readBoolean();
+ shouldShowEducation = parcel.readBoolean();
selectedBubbleKey = parcel.readString();
addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
BubbleInfo.class);
@@ -95,6 +97,7 @@ public class BubbleBarUpdate implements Parcelable {
return "BubbleBarUpdate{ expandedChanged=" + expandedChanged
+ " expanded=" + expanded
+ " selectedBubbleKey=" + selectedBubbleKey
+ + " shouldShowEducation=" + shouldShowEducation
+ " addedBubble=" + addedBubble
+ " updatedBubble=" + updatedBubble
+ " suppressedBubbleKey=" + suppressedBubbleKey
@@ -114,6 +117,7 @@ public class BubbleBarUpdate implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeBoolean(expandedChanged);
parcel.writeBoolean(expanded);
+ parcel.writeBoolean(shouldShowEducation);
parcel.writeString(selectedBubbleKey);
parcel.writeParcelable(addedBubble, flags);
parcel.writeParcelable(updatedBubble, flags);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
index 6a46d23ad2a1..0329b8df7544 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.bubble
+package com.android.wm.shell.common.bubbles;
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+/**
+ * Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
+ */
+public class BubbleConstants {
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
- OpenActivityFromBubbleTest(flicker)
+ /** The alpha for the scrim shown when bubbles are expanded. */
+ public static float BUBBLE_EXPANDED_SCRIM_ALPHA = .32f;
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
index 1fd22d0a3505..887af17c9653 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -31,7 +31,7 @@ import kotlin.math.sin
import kotlin.properties.Delegates
/** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */
-class BubblePopupDrawable(private val config: Config) : Drawable() {
+class BubblePopupDrawable(val config: Config) : Drawable() {
/** The direction of the arrow in the popup drawable */
enum class ArrowDirection {
UP,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
index f8a4946bb5c5..444fbf7884be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
@@ -29,7 +29,8 @@ constructor(
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
- private var popupDrawable: BubblePopupDrawable? = null
+ var popupDrawable: BubblePopupDrawable? = null
+ private set
/**
* Sets up the popup drawable with the config provided. Required to remove dependency on local
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
index d275a0be8e93..2eb55e19a960 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
@@ -49,6 +49,8 @@ class DismissView(context: Context) : FrameLayout(context) {
* @see [setup] method
*/
data class Config(
+ /** The resource id to set on the dismiss target circle view */
+ val dismissViewResId: Int,
/** dimen resource id of the dismiss target circle view size */
@DimenRes val targetSizeResId: Int,
/** dimen resource id of the icon size in the dismiss target */
@@ -121,6 +123,7 @@ class DismissView(context: Context) : FrameLayout(context) {
setBackgroundDrawable(gradientDrawable)
// Setup DismissCircleView
+ circle.id = config.dismissViewResId
circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId)
val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId)
circle.layoutParams = LayoutParams(targetSize, targetSize,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 1901e0bbe700..f9a286ec804f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -20,6 +20,15 @@ import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -28,6 +37,8 @@ import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.DisplayInfo;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
/**
@@ -195,6 +206,21 @@ public class DividerSnapAlgorithm {
}
}
+ /**
+ * Gets the SnapTarget corresponding to the given {@link SnapPosition}, or null if no such
+ * SnapTarget exists.
+ */
+ @Nullable
+ public SnapTarget findSnapTarget(@SnapPosition int snapPosition) {
+ for (SnapTarget t : mTargets) {
+ if (t.snapPosition == snapPosition) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+
public float calculateDismissingFraction(int position) {
if (position < mFirstSplitTarget.position) {
return 1f - (float) (position - getStartInset())
@@ -263,7 +289,7 @@ public class DividerSnapAlgorithm {
private SnapTarget snap(int position, boolean hardDismiss) {
if (shouldApplyFreeSnapMode(position)) {
- return new SnapTarget(position, position, SnapTarget.FLAG_NONE);
+ return new SnapTarget(position, position, SNAP_TO_NONE);
}
int minIndex = -1;
float minDistance = Float.MAX_VALUE;
@@ -291,8 +317,7 @@ public class DividerSnapAlgorithm {
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
}
- mTargets.add(new SnapTarget(startPos, startPos, SnapTarget.FLAG_DISMISS_START,
- 0.35f));
+ mTargets.add(new SnapTarget(startPos, startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
addRatio16_9Targets(isHorizontalDivision, dividerMax);
@@ -307,15 +332,15 @@ public class DividerSnapAlgorithm {
addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
- mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
+ mTargets.add(new SnapTarget(dividerMax, dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
}
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
int bottomPosition, int dividerMax) {
- maybeAddTarget(topPosition, topPosition - getStartInset());
+ maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_30_70);
addMiddleTarget(isHorizontalDivision);
maybeAddTarget(bottomPosition,
- dividerMax - getEndInset() - (bottomPosition + mDividerSize));
+ dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_70_30);
}
private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
@@ -349,16 +374,16 @@ public class DividerSnapAlgorithm {
* Adds a target at {@param position} but only if the area with size of {@param smallerSize}
* meets the minimal size requirement.
*/
- private void maybeAddTarget(int position, int smallerSize) {
+ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
if (smallerSize >= mMinimalSizeResizableTask) {
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, snapPosition));
}
}
private void addMiddleTarget(boolean isHorizontalDivision) {
int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, SNAP_TO_50_50));
}
private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
@@ -372,7 +397,7 @@ public class DividerSnapAlgorithm {
position = mDisplayWidth - position - mInsets.right - mDividerSize;
}
}
- mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(position, position, SNAP_TO_MINIMIZE));
}
public SnapTarget getMiddleTarget() {
@@ -412,6 +437,13 @@ public class DividerSnapAlgorithm {
}
/**
+ * Finds the {@link SnapPosition} nearest to the given position.
+ */
+ public int calculateNearestSnapPosition(int currentPosition) {
+ return snap(currentPosition, /* hardDismiss */ true).snapPosition;
+ }
+
+ /**
* Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
* if {@param increment} is negative and moves right otherwise.
*/
@@ -435,14 +467,6 @@ public class DividerSnapAlgorithm {
* Represents a snap target for the divider.
*/
public static class SnapTarget {
- public static final int FLAG_NONE = 0;
-
- /** If the divider reaches this value, the left/top task should be dismissed. */
- public static final int FLAG_DISMISS_START = 1;
-
- /** If the divider reaches this value, the right/bottom task should be dismissed */
- public static final int FLAG_DISMISS_END = 2;
-
/** Position of this snap target. The right/bottom edge of the top/left task snaps here. */
public final int position;
@@ -452,7 +476,10 @@ public class DividerSnapAlgorithm {
*/
public final int taskPosition;
- public final int flag;
+ /**
+ * An int describing the placement of the divider in this snap target.
+ */
+ public final @SnapPosition int snapPosition;
public boolean isMiddleTarget;
@@ -462,14 +489,15 @@ public class DividerSnapAlgorithm {
*/
private final float distanceMultiplier;
- public SnapTarget(int position, int taskPosition, int flag) {
- this(position, taskPosition, flag, 1f);
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) {
+ this(position, taskPosition, snapPosition, 1f);
}
- public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) {
+ public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition,
+ float distanceMultiplier) {
this.position = position;
this.taskPosition = taskPosition;
- this.flag = flag;
+ this.snapPosition = snapPosition;
this.distanceMultiplier = distanceMultiplier;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 755dba0c895f..26b5a5052594 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -26,10 +26,10 @@ import static android.view.WindowManager.DOCKED_TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
-import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
-import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -67,6 +67,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import java.io.PrintWriter;
@@ -116,7 +117,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
- private int mDividePosition;
+ private int mDividerPosition;
private boolean mInitialized = false;
private boolean mFreezeDividerWindow = false;
private int mOrientation;
@@ -268,7 +269,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
int getDividePosition() {
- return mDividePosition;
+ return mDividerPosition;
+ }
+
+ /**
+ * Finds the {@link SnapPosition} nearest to the current divider position.
+ */
+ public int calculateCurrentSnapPosition() {
+ return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition);
}
/**
@@ -345,16 +353,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
private void initDividerPosition(Rect oldBounds) {
- final float snapRatio = (float) mDividePosition
+ final float snapRatio = (float) mDividerPosition
/ (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
// Estimate position by previous ratio.
final float length =
(float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
final int estimatePosition = (int) (length * snapRatio);
// Init divider position by estimated position using current bounds snap algorithm.
- mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+ mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
estimatePosition).position;
- updateBounds(mDividePosition);
+ updateBounds(mDividerPosition);
}
private void updateBounds(int position) {
@@ -468,27 +476,29 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
void setDividePosition(int position, boolean applyLayoutChange) {
- mDividePosition = position;
- updateBounds(mDividePosition);
+ mDividerPosition = position;
+ updateBounds(mDividerPosition);
if (applyLayoutChange) {
mSplitLayoutHandler.onLayoutSizeChanged(this);
}
}
/** Updates divide position and split bounds base on the ratio within root bounds. */
- public void setDivideRatio(float ratio) {
- final int position = isLandscape()
- ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
- : mRootBounds.top + (int) (mRootBounds.height() * ratio);
- final DividerSnapAlgorithm.SnapTarget snapTarget =
- mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ public void setDivideRatio(@SnapPosition int snapPosition) {
+ final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget(
+ snapPosition);
+
+ if (snapTarget == null) {
+ throw new IllegalArgumentException("No SnapTarget for position " + snapPosition);
+ }
+
setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
public void resetDividerPosition() {
- mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
- updateBounds(mDividePosition);
+ mDividerPosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+ updateBounds(mDividerPosition);
mWinToken1 = null;
mWinToken2 = null;
mWinBounds1.setEmpty();
@@ -511,13 +521,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
* target indicates dismissing split.
*/
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
- switch (snapTarget.flag) {
- case FLAG_DISMISS_START:
+ switch (snapTarget.snapPosition) {
+ case SNAP_TO_START_AND_DISMISS:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
break;
- case FLAG_DISMISS_END:
+ case SNAP_TO_END_AND_DISMISS:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
@@ -669,8 +679,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onAnimationEnd(Animator animation) {
- mDividePosition = dividerPos;
- updateBounds(mDividePosition);
+ mDividerPosition = dividerPos;
+ updateBounds(mDividerPosition);
finishCallback.accept(insets);
InteractionJankMonitorUtils.endTracing(CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index be1b9b1227de..ff38b7e70410 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -57,6 +57,38 @@ public class SplitScreenConstants {
public @interface SplitPosition {
}
+ /** The divider doesn't snap to any target and is freely placeable. */
+ public static final int SNAP_TO_NONE = 0;
+
+ /** A snap target positioned near the screen edge for a minimized task */
+ public static final int SNAP_TO_MINIMIZE = 1;
+
+ /** If the divider reaches this value, the left/top task should be dismissed. */
+ public static final int SNAP_TO_START_AND_DISMISS = 2;
+
+ /** A snap target in the first half of the screen, where the split is roughly 30-70. */
+ public static final int SNAP_TO_30_70 = 3;
+
+ /** The 50-50 snap target */
+ public static final int SNAP_TO_50_50 = 4;
+
+ /** A snap target in the latter half of the screen, where the split is roughly 70-30. */
+ public static final int SNAP_TO_70_30 = 5;
+
+ /** If the divider reaches this value, the right/bottom task should be dismissed. */
+ public static final int SNAP_TO_END_AND_DISMISS = 6;
+
+ @IntDef(prefix = { "SNAP_TO_" }, value = {
+ SNAP_TO_NONE,
+ SNAP_TO_MINIMIZE,
+ SNAP_TO_START_AND_DISMISS,
+ SNAP_TO_30_70,
+ SNAP_TO_50_50,
+ SNAP_TO_70_30,
+ SNAP_TO_END_AND_DISMISS
+ })
+ public @interface SnapPosition {}
+
public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
public static final int[] CONTROLLED_WINDOWING_MODES =
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 9facbd542e6c..b52a118c7f1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -49,11 +49,11 @@ import dagger.Provides;
import java.util.Optional;
/**
- * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
- * accessible from components within the WM subcomponent (can be explicitly exposed to the
- * SysUIComponent, see {@link WMComponent}).
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only accessible
+ * from components within the WM subcomponent (can be explicitly exposed to the SysUIComponent, see
+ * {@link com.android.systemui.dagger.WMComponent}).
*
- * This module only defines Shell dependencies for the TV SystemUI implementation. Common
+ * <p>This module only defines Shell dependencies for the TV SystemUI implementation. Common
* dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = {TvPipModule.class})
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 45869e177c6d..998cd5d08c72 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
@@ -38,6 +38,7 @@ import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.back.BackAnimationBackground;
import com.android.wm.shell.back.BackAnimationController;
+import com.android.wm.shell.back.ShellBackAnimationRegistry;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DevicePostureController;
@@ -71,7 +72,6 @@ import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -108,19 +108,19 @@ import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import java.util.Optional;
+
import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
- * SysUIComponent, see {@link WMComponent}).
+ * SysUIComponent, see {@link com.android.systemui.dagger.WMComponent}).
*
- * This module only defines *common* dependencies across various SystemUI implementations,
+ * <p>This module only defines *common* dependencies across various SystemUI implementations,
* dependencies that are device/form factor SystemUI implementation specific should go into their
* respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
*/
@@ -322,16 +322,26 @@ public abstract class WMShellBaseModule {
ShellController shellController,
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler,
- BackAnimationBackground backAnimationBackground
- ) {
+ BackAnimationBackground backAnimationBackground,
+ Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry) {
if (BackAnimationController.IS_ENABLED) {
- return Optional.of(
- new BackAnimationController(shellInit, shellController, shellExecutor,
- backgroundHandler, context, backAnimationBackground));
+ return shellBackAnimationRegistry.map(
+ (animations) ->
+ new BackAnimationController(
+ shellInit,
+ shellController,
+ shellExecutor,
+ backgroundHandler,
+ context,
+ backAnimationBackground,
+ animations));
}
return Optional.empty();
}
+ @BindsOptionalOf
+ abstract ShellBackAnimationRegistry optionalBackAnimationRegistry();
+
//
// PiP (optional feature)
//
@@ -789,30 +799,10 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static Optional<DesktopMode> provideDesktopMode(
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController) {
- if (DesktopModeStatus.isProto2Enabled()) {
- return desktopTasksController.map(DesktopTasksController::asDesktopMode);
- }
- return desktopModeController.map(DesktopModeController::asDesktopMode);
+ return desktopTasksController.map(DesktopTasksController::asDesktopMode);
}
- @BindsOptionalOf
- @DynamicOverride
- abstract DesktopModeController optionalDesktopModeController();
-
- @WMSingleton
- @Provides
- static Optional<DesktopModeController> provideDesktopModeController(
- @DynamicOverride Optional<Lazy<DesktopModeController>> desktopModeController) {
- // Use optional-of-lazy for the dependency that this provider relies on.
- // Lazy ensures that this provider will not be the cause the dependency is created
- // when it will not be returned due to the condition below.
- if (DesktopModeStatus.isProto1Enabled()) {
- return desktopModeController.map(Lazy::get);
- }
- return Optional.empty();
- }
@BindsOptionalOf
@DynamicOverride
@@ -825,7 +815,7 @@ public abstract class WMShellBaseModule {
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
return desktopTasksController.map(Lazy::get);
}
return Optional.empty();
@@ -842,7 +832,7 @@ public abstract class WMShellBaseModule {
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
return desktopModeTaskRepository.map(Lazy::get);
}
return Optional.empty();
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 065e7b09a05d..9f9854e7e244 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
@@ -35,6 +35,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleEducationController;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.properties.ProdBubbleProperties;
@@ -52,8 +53,8 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
-import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -100,17 +101,19 @@ import java.util.List;
import java.util.Optional;
/**
- * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
- * accessible from components within the WM subcomponent (can be explicitly exposed to the
- * SysUIComponent, see {@link WMComponent}).
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only accessible
+ * from components within the WM subcomponent (can be explicitly exposed to the SysUIComponent, see
+ * {@link WMComponent}).
*
- * This module only defines Shell dependencies for handheld SystemUI implementation. Common
+ * <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common
* dependencies should go into {@link WMShellBaseModule}.
*/
-@Module(includes = {
- WMShellBaseModule.class,
- PipModule.class
-})
+@Module(
+ includes = {
+ WMShellBaseModule.class,
+ PipModule.class,
+ ShellBackAnimationModule.class,
+ })
public abstract class WMShellModule {
//
@@ -132,11 +135,18 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static BubbleEducationController provideBubbleEducationProvider(Context context) {
+ return new BubbleEducationController(context);
+ }
+
+ @WMSingleton
+ @Provides
static BubbleData provideBubbleData(Context context,
BubbleLogger logger,
BubblePositioner positioner,
+ BubbleEducationController educationController,
@ShellMainThread ShellExecutor mainExecutor) {
- return new BubbleData(context, logger, positioner, mainExecutor);
+ return new BubbleData(context, logger, positioner, educationController, mainExecutor);
}
// Note: Handler needed for LauncherApps.register
@@ -164,6 +174,7 @@ public abstract class WMShellModule {
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
+ Transitions transitions,
SyncTransactionQueue syncQueue,
IWindowManager wmService) {
return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
@@ -173,7 +184,8 @@ public abstract class WMShellModule {
statusBarService, windowManager, windowManagerShellWrapper, userManager,
launcherApps, logger, taskStackListener, organizer, positioner, displayController,
oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
- taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE);
+ taskViewTransitions, transitions, syncQueue, wmService,
+ ProdBubbleProperties.INSTANCE);
}
//
@@ -192,9 +204,10 @@ public abstract class WMShellModule {
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController) {
- if (DesktopModeStatus.isAnyEnabled()) {
+ Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ if (DesktopModeStatus.isEnabled()) {
return new DesktopModeWindowDecorViewModel(
context,
mainHandler,
@@ -205,8 +218,9 @@ public abstract class WMShellModule {
shellController,
syncQueue,
transitions,
- desktopModeController,
- desktopTasksController);
+ desktopTasksController,
+ recentsTransitionHandler,
+ rootTaskDisplayAreaOrganizer);
}
return new CaptionWindowDecorViewModel(
context,
@@ -346,13 +360,12 @@ public abstract class WMShellModule {
@Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsTransitionHandler,
KeyguardTransitionHandler keyguardTransitionHandler,
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
Optional<UnfoldTransitionHandler> unfoldHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTransitionController, recentsTransitionHandler,
- keyguardTransitionHandler, desktopModeController, desktopTasksController,
+ keyguardTransitionHandler, desktopTasksController,
unfoldHandler);
}
@@ -464,24 +477,6 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
@DynamicOverride
- static DesktopModeController provideDesktopModeController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Transitions transitions,
- @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
- @ShellMainThread Handler mainHandler,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
- return new DesktopModeController(context, shellInit, shellController, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler,
- mainExecutor);
- }
-
- @WMSingleton
- @Provides
- @DynamicOverride
static DesktopTasksController provideDesktopTasksController(
Context context,
ShellInit shellInit,
@@ -546,8 +541,7 @@ public abstract class WMShellModule {
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
- DefaultMixedHandler defaultMixedHandler,
- Optional<DesktopModeController> desktopModeController) {
+ DefaultMixedHandler defaultMixedHandler) {
return new Object();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
new file mode 100644
index 000000000000..b34c6b213df4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.dagger.back;
+
+import com.android.wm.shell.back.CrossActivityAnimation;
+import com.android.wm.shell.back.CrossTaskBackAnimation;
+import com.android.wm.shell.back.CustomizeActivityAnimation;
+import com.android.wm.shell.back.ShellBackAnimation;
+import com.android.wm.shell.back.ShellBackAnimationRegistry;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/** Default animation definitions for predictive back. */
+@Module
+public interface ShellBackAnimationModule {
+ /** Default animation registry */
+ @Provides
+ static ShellBackAnimationRegistry provideBackAnimationRegistry(
+ @ShellBackAnimation.CrossActivity ShellBackAnimation crossActivity,
+ @ShellBackAnimation.CrossTask ShellBackAnimation crossTask,
+ @ShellBackAnimation.CustomizeActivity ShellBackAnimation customizeActivity) {
+ return new ShellBackAnimationRegistry(
+ crossActivity,
+ crossTask,
+ customizeActivity,
+ /* defaultBackToHomeAnimation= */ null);
+ }
+
+ /** Default cross activity back animation */
+ @Binds
+ @ShellBackAnimation.CrossActivity
+ ShellBackAnimation bindCrossActivityShellBackAnimation(
+ CrossActivityAnimation crossActivityAnimation);
+
+ /** Default cross task back animation */
+ @Binds
+ @ShellBackAnimation.CrossTask
+ ShellBackAnimation provideCrossTaskShellBackAnimation(
+ CrossTaskBackAnimation crossTaskBackAnimation);
+
+ /** Default customized activity back animation */
+ @Binds
+ @ShellBackAnimation.CustomizeActivity
+ ShellBackAnimation provideCustomizeActivityShellBackAnimation(
+ CustomizeActivityAnimation customizeActivityAnimation);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
deleted file mode 100644
index 5b24d7a60c4e..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ /dev/null
@@ -1,557 +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.wm.shell.desktopmode;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.ContentObserver;
-import android.graphics.Region;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.ArraySet;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.DisplayAreaInfo;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ExternalInterfaceBinder;
-import com.android.wm.shell.common.RemoteCallable;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * Handles windowing changes when desktop mode system setting changes
- */
-public class DesktopModeController implements RemoteCallable<DesktopModeController>,
- Transitions.TransitionHandler {
-
- private final Context mContext;
- private final ShellController mShellController;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- private final Transitions mTransitions;
- private final DesktopModeTaskRepository mDesktopModeTaskRepository;
- private final ShellExecutor mMainExecutor;
- private final DesktopModeImpl mDesktopModeImpl = new DesktopModeImpl();
- private final SettingsObserver mSettingsObserver;
-
- public DesktopModeController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Transitions transitions,
- DesktopModeTaskRepository desktopModeTaskRepository,
- @ShellMainThread Handler mainHandler,
- @ShellMainThread ShellExecutor mainExecutor) {
- mContext = context;
- mShellController = shellController;
- mShellTaskOrganizer = shellTaskOrganizer;
- mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
- mTransitions = transitions;
- mDesktopModeTaskRepository = desktopModeTaskRepository;
- mMainExecutor = mainExecutor;
- mSettingsObserver = new SettingsObserver(mContext, mainHandler);
- if (DesktopModeStatus.isProto1Enabled()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- private void onInit() {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
- mShellController.addExternalInterface(KEY_EXTRA_SHELL_DESKTOP_MODE,
- this::createExternalInterface, this);
- mSettingsObserver.observe();
- if (DesktopModeStatus.isActive(mContext)) {
- updateDesktopModeActive(true);
- }
- mTransitions.addHandler(this);
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
-
- /**
- * Get connection interface between sysui and shell
- */
- public DesktopMode asDesktopMode() {
- return mDesktopModeImpl;
- }
-
- /**
- * Creates a new instance of the external interface to pass to another process.
- */
- private ExternalInterfaceBinder createExternalInterface() {
- return new IDesktopModeImpl(this);
- }
-
- /**
- * Adds a listener to find out about changes in the visibility of freeform tasks.
- *
- * @param listener the listener to add.
- * @param callbackExecutor the executor to call the listener on.
- */
- public void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener,
- Executor callbackExecutor) {
- mDesktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor);
- }
-
- /**
- * Adds a listener to track changes to corners of desktop mode tasks.
- * @param listener the listener to add.
- * @param callbackExecutor the executor to call the listener on.
- */
- public void addTaskCornerListener(Consumer<Region> listener,
- Executor callbackExecutor) {
- mDesktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor);
- }
-
- @VisibleForTesting
- void updateDesktopModeActive(boolean active) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
-
- int displayId = mContext.getDisplayId();
-
- ArrayList<RunningTaskInfo> runningTasks = mShellTaskOrganizer.getRunningTasks(displayId);
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- // Reset freeform windowing mode that is set per task level so tasks inherit it
- clearFreeformForStandardTasks(runningTasks, wct);
- if (active) {
- moveHomeBehindVisibleTasks(runningTasks, wct);
- setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FREEFORM, wct);
- } else {
- clearBoundsForStandardTasks(runningTasks, wct);
- setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FULLSCREEN, wct);
- }
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitions.startTransition(TRANSIT_CHANGE, wct, null);
- } else {
- mRootTaskDisplayAreaOrganizer.applyTransaction(wct);
- }
- }
-
- private WindowContainerTransaction clearBoundsForStandardTasks(
- ArrayList<RunningTaskInfo> runningTasks, WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks");
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
- taskInfo.token, taskInfo);
- wct.setBounds(taskInfo.token, null);
- }
- }
- return wct;
- }
-
- private void clearFreeformForStandardTasks(ArrayList<RunningTaskInfo> runningTasks,
- WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks");
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE,
- "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
- taskInfo);
- wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- }
- }
- }
-
- private void moveHomeBehindVisibleTasks(ArrayList<RunningTaskInfo> runningTasks,
- WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks");
- RunningTaskInfo homeTask = null;
- ArrayList<RunningTaskInfo> visibleTasks = new ArrayList<>();
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
- homeTask = taskInfo;
- } else if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
- && taskInfo.isVisible()) {
- visibleTasks.add(taskInfo);
- }
- }
- if (homeTask == null) {
- ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: home task not found");
- } else {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: visible tasks %d",
- visibleTasks.size());
- wct.reorder(homeTask.getToken(), true /* onTop */);
- for (RunningTaskInfo task : visibleTasks) {
- wct.reorder(task.getToken(), true /* onTop */);
- }
- }
- }
-
- private void setDisplayAreaWindowingMode(int displayId,
- @WindowConfiguration.WindowingMode int windowingMode, WindowContainerTransaction wct) {
- DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
- displayId);
- if (displayAreaInfo == null) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE,
- "unable to update windowing mode for display %d display not found", displayId);
- return;
- }
-
- ProtoLog.v(WM_SHELL_DESKTOP_MODE,
- "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId,
- displayAreaInfo.configuration.windowConfiguration.getWindowingMode(),
- windowingMode);
-
- wct.setWindowingMode(displayAreaInfo.token, windowingMode);
- }
-
- /**
- * Show apps on desktop
- */
- void showDesktopApps(int displayId) {
- // Bring apps to front, ignoring their visibility status to always ensure they are on top.
- WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(displayId, wct);
-
- if (!wct.isEmpty()) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/268662477): add animation for the transition
- mTransitions.startTransition(TRANSIT_NONE, wct, null /* handler */);
- } else {
- mShellTaskOrganizer.applyTransaction(wct);
- }
- }
- }
-
- /** Get number of tasks that are marked as visible */
- int getVisibleTaskCount(int displayId) {
- return mDesktopModeTaskRepository.getVisibleTaskCount(displayId);
- }
-
- private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) {
- final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId);
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
-
- final List<RunningTaskInfo> taskInfos = new ArrayList<>();
- for (Integer taskId : activeTasks) {
- RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId);
- if (taskInfo != null) {
- taskInfos.add(taskInfo);
- }
- }
-
- if (taskInfos.isEmpty()) {
- return;
- }
-
- moveHomeTaskToFront(wct);
-
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "bringDesktopAppsToFront: reordering all active tasks to the front");
- final List<Integer> allTasksInZOrder =
- mDesktopModeTaskRepository.getFreeformTasksInZOrder();
- // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last
- // in the WCT.
- taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId)));
- for (RunningTaskInfo task : taskInfos) {
- wct.reorder(task.token, true);
- }
- }
-
- private void moveHomeTaskToFront(WindowContainerTransaction wct) {
- for (RunningTaskInfo task : mShellTaskOrganizer.getRunningTasks(mContext.getDisplayId())) {
- if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
- wct.reorder(task.token, true /* onTop */);
- return;
- }
- }
- }
-
- /**
- * Update corner rects stored for a specific task
- * @param taskId task to update
- * @param taskCorners task's new corner handles
- */
- public void onTaskCornersChanged(int taskId, Region taskCorners) {
- mDesktopModeTaskRepository.updateTaskCorners(taskId, taskCorners);
- }
-
- /**
- * Remove corners saved for a task. Likely used due to task closure.
- * @param taskId task to remove
- */
- public void removeCornersForTask(int taskId) {
- mDesktopModeTaskRepository.removeTaskCorners(taskId);
- }
-
- /**
- * Moves a specifc task to the front.
- * @param taskInfo the task to show in front.
- */
- public void moveTaskToFront(RunningTaskInfo taskInfo) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(taskInfo.token, true /* onTop */);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitions.startTransition(TRANSIT_TO_FRONT, wct, null);
- } else {
- mShellTaskOrganizer.applyTransaction(wct);
- }
- }
-
- /**
- * Turn desktop mode on or off
- * @param active the desired state for desktop mode setting
- */
- public void setDesktopModeActive(boolean active) {
- int value = active ? 1 : 0;
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value);
- }
-
- /**
- * Returns the windowing mode of the display area with the specified displayId.
- * @param displayId
- * @return
- */
- public int getDisplayAreaWindowingMode(int displayId) {
- return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
- .configuration.windowConfiguration.getWindowingMode();
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- // This handler should never be the sole handler, so should not animate anything.
- return false;
- }
-
- @Nullable
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- RunningTaskInfo triggerTask = request.getTriggerTask();
- // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in
- // freeform
- if (!DesktopModeStatus.isActive(mContext)) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "skip shell transition request: desktop mode not active");
- return null;
- }
- if (request.getType() != TRANSIT_OPEN && request.getType() != TRANSIT_TO_FRONT) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "skip shell transition request: unsupported type %s",
- WindowManager.transitTypeToString(request.getType()));
- return null;
- }
- if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task");
- return null;
- }
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request);
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(triggerTask.displayId, wct);
- wct.reorder(triggerTask.token, true /* onTop */);
-
- return wct;
- }
-
- /**
- * Applies the proper surface states (rounded corners) to tasks when desktop mode is active.
- * This is intended to be used when desktop mode is part of another animation but isn't, itself,
- * animating.
- */
- public void syncSurfaceState(@NonNull TransitionInfo info,
- SurfaceControl.Transaction finishTransaction) {
- // Add rounded corners to freeform windows
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.dialogCornerRadius});
- final int cornerRadius = ta.getDimensionPixelSize(0, 0);
- ta.recycle();
- for (TransitionInfo.Change change: info.getChanges()) {
- if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- finishTransaction.setCornerRadius(change.getLeash(), cornerRadius);
- }
- }
- }
-
- /**
- * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
- */
- private final class SettingsObserver extends ContentObserver {
-
- private final Uri mDesktopModeSetting = Settings.System.getUriFor(
- Settings.System.DESKTOP_MODE);
-
- private final Context mContext;
-
- SettingsObserver(Context context, Handler handler) {
- super(handler);
- mContext = context;
- }
-
- public void observe() {
- // TODO(b/242867463): listen for setting change for all users
- mContext.getContentResolver().registerContentObserver(mDesktopModeSetting,
- false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT);
- }
-
- @Override
- public void onChange(boolean selfChange, @Nullable Uri uri) {
- if (mDesktopModeSetting.equals(uri)) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting");
- desktopModeSettingChanged();
- }
- }
-
- private void desktopModeSettingChanged() {
- boolean enabled = DesktopModeStatus.isActive(mContext);
- updateDesktopModeActive(enabled);
- }
- }
-
- /**
- * The interface for calls from outside the shell, within the host process.
- */
- @ExternalThread
- private final class DesktopModeImpl implements DesktopMode {
-
- @Override
- public void addVisibleTasksListener(
- DesktopModeTaskRepository.VisibleTasksListener listener,
- Executor callbackExecutor) {
- mMainExecutor.execute(() -> {
- DesktopModeController.this.addVisibleTasksListener(listener, callbackExecutor);
- });
- }
-
- @Override
- public void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
- Executor callbackExecutor) {
- mMainExecutor.execute(() -> {
- DesktopModeController.this.addTaskCornerListener(listener, callbackExecutor);
- });
- }
- }
-
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class IDesktopModeImpl extends IDesktopMode.Stub
- implements ExternalInterfaceBinder {
-
- private DesktopModeController mController;
-
- IDesktopModeImpl(DesktopModeController controller) {
- mController = controller;
- }
-
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- @Override
- public void invalidate() {
- mController = null;
- }
-
- @Override
- public void showDesktopApps(int displayId) {
- executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
- controller -> controller.showDesktopApps(displayId));
- }
-
- @Override
- public void showDesktopApp(int taskId) throws RemoteException {
- // TODO
- }
-
- @Override
- public int getVisibleTaskCount(int displayId) throws RemoteException {
- int[] result = new int[1];
- executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
- controller -> result[0] = controller.getVisibleTaskCount(displayId),
- true /* blocking */
- );
- return result[0];
- }
-
- @Override
- public void onDesktopSplitSelectAnimComplete(RunningTaskInfo taskInfo) {
-
- }
-
- @Override
- public void stashDesktopApps(int displayId) throws RemoteException {
- // Stashing of desktop apps not needed. Apps always launch on desktop
- }
-
- @Override
- public void hideStashedDesktopApps(int displayId) throws RemoteException {
- // Stashing of desktop apps not needed. Apps always launch on desktop
- }
-
- @Override
- public void setTaskListener(IDesktopTaskListener listener) throws RemoteException {
- // TODO(b/261234402): move visibility from sysui state to listener
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 517f9f2aba27..77831136b0bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -16,14 +16,7 @@
package com.android.wm.shell.desktopmode;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
-
-import android.content.Context;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.protolog.common.ProtoLog;
/**
* Constants for desktop mode feature
@@ -31,13 +24,7 @@ import com.android.internal.protolog.common.ProtoLog;
public class DesktopModeStatus {
/**
- * Flag to indicate whether desktop mode is available on the device
- */
- private static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
-
- /**
- * Flag to indicate whether desktop mode proto 2 is available on the device
+ * Flag to indicate whether desktop mode proto is available on the device
*/
private static final boolean IS_PROTO2_ENABLED = SystemProperties.getBoolean(
"persist.wm.debug.desktop_mode_2", false);
@@ -64,28 +51,13 @@ public class DesktopModeStatus {
"persist.wm.debug.desktop_stashing", false);
/**
- * Return {@code true} if desktop mode support is enabled
- */
- public static boolean isProto1Enabled() {
- return IS_SUPPORTED;
- }
-
- /**
* Return {@code true} is desktop windowing proto 2 is enabled
*/
- public static boolean isProto2Enabled() {
+ public static boolean isEnabled() {
return IS_PROTO2_ENABLED;
}
/**
- * Return {@code true} if proto 1 or 2 is enabled.
- * Can be used to guard logic that is common for both prototypes.
- */
- public static boolean isAnyEnabled() {
- return isProto1Enabled() || isProto2Enabled();
- }
-
- /**
* Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
*/
public static boolean isVeiledResizeEnabled() {
@@ -99,26 +71,4 @@ public class DesktopModeStatus {
public static boolean isStashingEnabled() {
return IS_STASHING_ENABLED;
}
- /**
- * Check if desktop mode is active
- *
- * @return {@code true} if active
- */
- public static boolean isActive(Context context) {
- if (!isAnyEnabled()) {
- return false;
- }
- if (isProto2Enabled()) {
- // Desktop mode is always active in prototype 2
- return true;
- }
- try {
- int result = Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
- return result != 0;
- } catch (Exception e) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
- return false;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index c05af73e6765..c0fc02fadd4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -52,8 +52,8 @@ class DesktopModeTaskRepository {
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
// Track visible tasks separately because a task may be part of the desktop but not visible.
private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
- // Track corners of desktop tasks, used to determine gesture exclusion
- private val desktopCorners = SparseArray<Region>()
+ // Track corner/caption regions of desktop tasks, used to determine gesture exclusion
+ private val desktopExclusionRegions = SparseArray<Region>()
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
@@ -96,10 +96,11 @@ class DesktopModeTaskRepository {
}
/**
- * Add a Consumer which will inform other classes of changes to corners for all Desktop tasks.
+ * Add a Consumer which will inform other classes of changes to exclusion regions for all
+ * Desktop tasks.
*/
- fun setTaskCornerListener(cornersListener: Consumer<Region>, executor: Executor) {
- desktopGestureExclusionListener = cornersListener
+ fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
+ desktopGestureExclusionListener = regionListener
desktopGestureExclusionExecutor = executor
executor.execute {
desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
@@ -107,14 +108,14 @@ class DesktopModeTaskRepository {
}
/**
- * Create a new merged region representative of all corners in all desktop tasks.
+ * Create a new merged region representative of all exclusion regions in all desktop tasks.
*/
private fun calculateDesktopExclusionRegion(): Region {
- val desktopCornersRegion = Region()
- desktopCorners.valueIterator().forEach { taskCorners ->
- desktopCornersRegion.op(taskCorners, Region.Op.UNION)
+ val desktopExclusionRegion = Region()
+ desktopExclusionRegions.valueIterator().forEach { taskExclusionRegion ->
+ desktopExclusionRegion.op(taskExclusionRegion, Region.Op.UNION)
}
- return desktopCornersRegion
+ return desktopExclusionRegion
}
/**
@@ -294,22 +295,24 @@ class DesktopModeTaskRepository {
}
/**
- * Updates the active desktop corners; if desktopCorners has been accepted by
- * desktopCornersListener, it will be updated in the appropriate classes.
+ * Updates the active desktop gesture exclusion regions; if desktopExclusionRegions has been
+ * accepted by desktopGestureExclusionListener, it will be updated in the
+ * appropriate classes.
*/
- fun updateTaskCorners(taskId: Int, taskCorners: Region) {
- desktopCorners.put(taskId, taskCorners)
+ fun updateTaskExclusionRegions(taskId: Int, taskExclusionRegions: Region) {
+ desktopExclusionRegions.put(taskId, taskExclusionRegions)
desktopGestureExclusionExecutor?.execute {
desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
}
}
/**
- * Removes the active desktop corners for the specified task; if desktopCorners has been
- * accepted by desktopCornersListener, it will be updated in the appropriate classes.
+ * Removes the desktop gesture exclusion region for the specified task; if exclusionRegion
+ * has been accepted by desktopGestureExclusionListener, it will be updated in the
+ * appropriate classes.
*/
- fun removeTaskCorners(taskId: Int) {
- desktopCorners.delete(taskId)
+ fun removeExclusionRegion(taskId: Int) {
+ desktopExclusionRegions.delete(taskId)
desktopGestureExclusionExecutor?.execute {
desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
}
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 4e418e11d226..f8dd208f96db 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
@@ -16,9 +16,7 @@
package com.android.wm.shell.desktopmode
-import android.R
import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -27,7 +25,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
-import android.content.res.TypedArray
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -44,6 +41,7 @@ import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
@@ -62,6 +60,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksLi
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -109,18 +108,20 @@ class DesktopTasksController(
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height)
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height
+ )
private val transitionAreaWidth
get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_width)
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
+ )
// This is public to avoid cyclic dependency; it is set by SplitScreenController
lateinit var splitScreenController: SplitScreenController
init {
desktopMode = DesktopModeImpl()
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
shellInit.addInitCallback({ onInit() }, this)
}
}
@@ -212,6 +213,7 @@ class DesktopTasksController(
"DesktopTasksController: moveToDesktop taskId=%d",
task.taskId
)
+ exitSplitIfApplicable(wct, task)
// Bring other apps to front first
bringDesktopAppsToFront(task.displayId, wct)
addMoveToDesktopChanges(wct, task)
@@ -239,6 +241,7 @@ class DesktopTasksController(
taskInfo.taskId
)
val wct = WindowContainerTransaction()
+ exitSplitIfApplicable(wct, taskInfo)
moveHomeTaskToFront(wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, startBounds)
@@ -318,7 +321,7 @@ class DesktopTasksController(
task.taskId
)
val wct = WindowContainerTransaction()
- wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+ wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW)
wct.setBounds(task.token, Rect())
wct.setDensityDpi(task.token, getDefaultDensityDpi())
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -328,6 +331,13 @@ class DesktopTasksController(
}
}
+ private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
+ if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ splitScreenController.prepareExitSplitScreen(wct,
+ splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+ }
+ }
+
/**
* The second part of the animated move to desktop transition, called after
* {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
@@ -493,6 +503,55 @@ class DesktopTasksController(
}
}
+ /**
+ * Quick-resize to the right or left half of the stable bounds.
+ *
+ * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
+ */
+ fun snapToHalfScreen(
+ taskInfo: RunningTaskInfo,
+ windowDecor: DesktopModeWindowDecoration,
+ position: SnapPosition
+ ) {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+
+ val destinationWidth = stableBounds.width() / 2
+ val destinationBounds = when (position) {
+ SnapPosition.LEFT -> {
+ Rect(
+ stableBounds.left,
+ stableBounds.top,
+ stableBounds.left + destinationWidth,
+ stableBounds.bottom
+ )
+ }
+ SnapPosition.RIGHT -> {
+ Rect(
+ stableBounds.right - destinationWidth,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom
+ )
+ }
+ }
+
+ if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
+
+ val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ toggleResizeDesktopTaskTransitionHandler.startTransition(
+ wct,
+ taskInfo.taskId,
+ windowDecor
+ )
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) {
val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt()
val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt()
@@ -645,10 +704,7 @@ class DesktopTasksController(
finishTransaction: SurfaceControl.Transaction
) {
// Add rounded corners to freeform windows
- val ta: TypedArray = context.obtainStyledAttributes(
- intArrayOf(R.attr.dialogCornerRadius))
- val cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
- ta.recycle()
+ val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
@@ -921,17 +977,17 @@ class DesktopTasksController(
}
/**
- * Update the corner region for a specified task
+ * Update the exclusion region for a specified task
*/
- fun onTaskCornersChanged(taskId: Int, corner: Region) {
- desktopModeTaskRepository.updateTaskCorners(taskId, corner)
+ fun onExclusionRegionChanged(taskId: Int, exclusionRegion: Region) {
+ desktopModeTaskRepository.updateTaskExclusionRegions(taskId, exclusionRegion)
}
/**
- * Remove a previously tracked corner region for a specified task.
+ * Remove a previously tracked exclusion region for a specified task.
*/
- fun removeCornersForTask(taskId: Int) {
- desktopModeTaskRepository.removeTaskCorners(taskId)
+ fun removeExclusionRegionForTask(taskId: Int) {
+ desktopModeTaskRepository.removeExclusionRegion(taskId)
}
/**
@@ -945,16 +1001,16 @@ class DesktopTasksController(
}
/**
- * Adds a listener to track changes to desktop task corners
+ * Adds a listener to track changes to desktop task gesture exclusion regions
*
* @param listener the listener to add.
* @param callbackExecutor the executor to call the listener on.
*/
- fun setTaskCornerListener(
+ fun setTaskRegionListener(
listener: Consumer<Region>,
callbackExecutor: Executor
) {
- desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor)
+ desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor)
}
private fun dump(pw: PrintWriter, prefix: String) {
@@ -980,7 +1036,7 @@ class DesktopTasksController(
callbackExecutor: Executor
) {
mainExecutor.execute {
- this@DesktopTasksController.setTaskCornerListener(listener, callbackExecutor)
+ this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)
}
}
}
@@ -1117,4 +1173,7 @@ class DesktopTasksController(
return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE
}
}
+
+ /** The positions on a screen that a task can snap to. */
+ enum class SnapPosition { RIGHT, LEFT }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 1128d91bb5ad..024465b281b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -271,8 +271,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
startT.apply();
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null));
+ mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null));
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 22541bbd892a..a80241e0ac5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -68,7 +68,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
private void onInit() {
mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM);
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mShellTaskOrganizer.addFocusListener(this);
}
}
@@ -90,7 +90,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
t.apply();
}
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
if (taskInfo.isVisible) {
@@ -111,7 +111,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.removeFreeformTask(taskInfo.taskId);
if (repository.removeActiveTask(taskInfo.taskId)) {
@@ -135,7 +135,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
taskInfo.taskId);
mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
state.mTaskInfo = taskInfo;
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
if (taskInfo.isVisible) {
if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
@@ -154,7 +154,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
"Freeform Task Focus Changed: #%d focused=%b",
taskInfo.taskId, taskInfo.isFocused);
- if (DesktopModeStatus.isAnyEnabled() && taskInfo.isFocused) {
+ if (DesktopModeStatus.isEnabled() && taskInfo.isFocused) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index bc17ce97dbff..118ad9c4bfe3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -383,6 +383,9 @@ public class PipAccessibilityInteractionConnection {
}
@Override
- public void attachAccessibilityOverlayToWindow(SurfaceControl sc) {}
+ public void attachAccessibilityOverlayToWindow(
+ SurfaceControl sc,
+ int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
index 5f6b3fe1e250..fc0b876e1bde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
@@ -77,6 +77,19 @@ abstract class TvPipAction {
return mActionType;
}
+ static String getActionTypeString(@ActionType int actionType) {
+ switch (actionType) {
+ case ACTION_FULLSCREEN: return "ACTION_FULLSCREEN";
+ case ACTION_CLOSE: return "ACTION_CLOSE";
+ case ACTION_MOVE: return "ACTION_MOVE";
+ case ACTION_EXPAND_COLLAPSE: return "ACTION_EXPAND_COLLAPSE";
+ case ACTION_CUSTOM: return "ACTION_CUSTOM";
+ case ACTION_CUSTOM_CLOSE: return "ACTION_CUSTOM_CLOSE";
+ default:
+ return "UNDEFINED";
+ }
+ }
+
abstract void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler);
abstract PendingIntent getPendingIntent();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index 4bba9690707a..6b890c49b713 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -56,8 +56,10 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler {
private final List<Listener> mListeners = new ArrayList<>();
private final TvPipAction.SystemActionsHandler mSystemActionsHandler;
- private final List<TvPipAction> mActionsList;
+ private final List<TvPipAction> mActionsList = new ArrayList<>();
+ private final TvPipSystemAction mFullscreenAction;
private final TvPipSystemAction mDefaultCloseAction;
+ private final TvPipSystemAction mMoveAction;
private final TvPipSystemAction mExpandCollapseAction;
private final List<RemoteAction> mMediaActions = new ArrayList<>();
@@ -67,26 +69,27 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler {
TvPipAction.SystemActionsHandler systemActionsHandler) {
mSystemActionsHandler = systemActionsHandler;
- mActionsList = new ArrayList<>();
- mActionsList.add(new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
+ mFullscreenAction = new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context,
- mSystemActionsHandler));
-
+ mSystemActionsHandler);
mDefaultCloseAction = new TvPipSystemAction(ACTION_CLOSE, R.string.pip_close,
R.drawable.pip_ic_close_white, ACTION_CLOSE_PIP, context, mSystemActionsHandler);
- mActionsList.add(mDefaultCloseAction);
-
- mActionsList.add(new TvPipSystemAction(ACTION_MOVE, R.string.pip_move,
- R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler));
-
+ mMoveAction = new TvPipSystemAction(ACTION_MOVE, R.string.pip_move,
+ R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler);
mExpandCollapseAction = new TvPipSystemAction(ACTION_EXPAND_COLLAPSE, R.string.pip_collapse,
R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context,
mSystemActionsHandler);
- mActionsList.add(mExpandCollapseAction);
+ initActions();
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
+ private void initActions() {
+ mActionsList.add(mFullscreenAction);
+ mActionsList.add(mDefaultCloseAction);
+ mActionsList.add(mMoveAction);
+ }
+
@Override
public void executeAction(@TvPipAction.ActionType int actionType) {
if (mSystemActionsHandler != null) {
@@ -199,6 +202,14 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler {
}
}
+ void reset() {
+ mActionsList.clear();
+ mMediaActions.clear();
+ mAppActions.clear();
+
+ initActions();
+ }
+
List<TvPipAction> getActionsList() {
return mActionsList;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index 47a8cc89559e..2b3a93e3c3e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -81,6 +81,7 @@ public class TvPipBoundsState extends PipBoundsState {
super(context, sizeSpecSource, pipDisplayLayoutState);
mContext = context;
updateDefaultGravity();
+ mTvPipGravity = mDefaultGravity;
mPreviousCollapsedGravity = mDefaultGravity;
mIsTvExpandedPipSupported = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 46336ce0655a..72115fdefa05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -478,6 +478,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mActionBroadcastReceiver.unregister();
mTvPipMenuController.closeMenu();
+ mTvPipActionsProvider.reset();
mTvPipBoundsState.resetTvPipState();
mTvPipBoundsController.reset();
setState(STATE_NO_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b2a189b45d6c..ee55211a73a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -62,13 +62,16 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private SurfaceControl mLeash;
private TvPipMenuView mPipMenuView;
private TvPipBackgroundView mPipBackgroundView;
- private boolean mMenuIsFocused;
@TvPipMenuMode
private int mCurrentMenuMode = MODE_NO_MENU;
@TvPipMenuMode
private int mPrevMenuMode = MODE_NO_MENU;
+ /** When the window gains focus, enter this menu mode */
+ @TvPipMenuMode
+ private int mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+
@IntDef(prefix = { "MODE_" }, value = {
MODE_NO_MENU,
MODE_MOVE_MENU,
@@ -170,6 +173,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mPipMenuView = createTvPipMenuView();
setUpViewSurfaceZOrder(mPipMenuView, 1);
addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
+ mPipMenuView.getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> {
+ onPipWindowFocusChanged(hasFocus);
+ });
}
@VisibleForTesting
@@ -224,13 +230,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void showMovementMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showMovementMenu()", TAG);
- switchToMenuMode(MODE_MOVE_MENU);
+ requestMenuMode(MODE_MOVE_MENU);
}
@Override
public void showMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
- switchToMenuMode(MODE_ALL_ACTIONS_MENU, true);
+ mPipMenuView.resetMenu();
+ requestMenuMode(MODE_ALL_ACTIONS_MENU);
}
void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
@@ -250,7 +257,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void closeMenu() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: closeMenu()", TAG);
- switchToMenuMode(MODE_NO_MENU);
+ requestMenuMode(MODE_NO_MENU);
}
@Override
@@ -392,11 +399,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
}
- // Start methods handling {@link TvPipMenuMode}
+ // Beginning of convenience methods for {@link TvPipMenuMode}
@VisibleForTesting
boolean isMenuOpen() {
- return mCurrentMenuMode != MODE_NO_MENU;
+ return isMenuOpen(mCurrentMenuMode);
+ }
+
+ private static boolean isMenuOpen(@TvPipMenuMode int menuMode) {
+ return menuMode != MODE_NO_MENU;
}
@VisibleForTesting
@@ -409,31 +420,93 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
return mCurrentMenuMode == MODE_ALL_ACTIONS_MENU;
}
- private void switchToMenuMode(@TvPipMenuMode int menuMode) {
- switchToMenuMode(menuMode, false);
+ @VisibleForTesting
+ String getMenuModeString() {
+ return getMenuModeString(mCurrentMenuMode);
}
- private void switchToMenuMode(@TvPipMenuMode int menuMode, boolean resetMenu) {
- // Note: we intentionally don't return early here, because the TvPipMenuView needs to
- // refresh the Ui even if there is no menu mode change.
- mPrevMenuMode = mCurrentMenuMode;
- mCurrentMenuMode = menuMode;
+ static String getMenuModeString(@TvPipMenuMode int menuMode) {
+ switch(menuMode) {
+ case MODE_NO_MENU:
+ return "MODE_NO_MENU";
+ case MODE_MOVE_MENU:
+ return "MODE_MOVE_MENU";
+ case MODE_ALL_ACTIONS_MENU:
+ return "MODE_ALL_ACTIONS_MENU";
+ default:
+ return "Unknown";
+ }
+ }
+
+ // Beginning of methods handling switching between menu modes
+
+ private void requestMenuMode(@TvPipMenuMode int menuMode) {
+ if (isMenuOpen() == isMenuOpen(menuMode)) {
+ // No need to request a focus change. We can directly switch to the new mode.
+ switchToMenuMode(menuMode);
+ } else {
+ if (isMenuOpen(menuMode)) {
+ mMenuModeOnFocus = menuMode;
+ }
+
+ // Send a request to gain window focus if the menu is open, or lose window focus
+ // otherwise. Once the focus change happens, we will request the new mode in the
+ // callback {@link #onPipWindowFocusChanged}.
+ requestPipMenuFocus(isMenuOpen(menuMode));
+ }
+ // Note: we don't handle cases where there is a focus change currently in flight, because
+ // this is very unlikely to happen in practice and would complicate the logic.
+ }
+
+ private void requestPipMenuFocus(boolean focus) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: requestPipMenuFocus(%b)", TAG, focus);
+
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mSystemWindows.getFocusGrantToken(mPipMenuView), focus);
+ } catch (Exception e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Unable to update focus, %s", TAG, e);
+ }
+ }
+
+ /**
+ * Called when the menu window gains or loses focus.
+ */
+ @VisibleForTesting
+ void onPipWindowFocusChanged(boolean focused) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
+ switchToMenuMode(focused ? mMenuModeOnFocus : MODE_NO_MENU);
+
+ // Reset the default menu mode for focused state.
+ mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+ }
+
+ /**
+ * Immediately switches to the menu mode in the given request. Updates the mDelegate and the UI.
+ * Doesn't handle any focus changes.
+ */
+ private void switchToMenuMode(@TvPipMenuMode int menuMode) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: switchToMenuMode: from=%s, to=%s", TAG, getMenuModeString(),
+ getMenuModeString(menuMode));
- ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: switchToMenuMode: setting mCurrentMenuMode=%s, mPrevMenuMode=%s", TAG,
- getMenuModeString(), getMenuModeString(mPrevMenuMode));
+ if (mCurrentMenuMode == menuMode) return;
- updateUiOnNewMenuModeRequest(resetMenu);
+ mPrevMenuMode = mCurrentMenuMode;
+ mCurrentMenuMode = menuMode;
+ updateUiOnNewMenuModeRequest();
updateDelegateOnNewMenuModeRequest();
}
- private void updateUiOnNewMenuModeRequest(boolean resetMenu) {
+ private void updateUiOnNewMenuModeRequest() {
if (mPipMenuView == null || mPipBackgroundView == null) return;
mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity());
- mPipMenuView.transitionToMenuMode(mCurrentMenuMode, resetMenu);
+ mPipMenuView.transitionToMenuMode(mCurrentMenuMode);
mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode);
- grantPipMenuFocus(mCurrentMenuMode != MODE_NO_MENU);
}
private void updateDelegateOnNewMenuModeRequest() {
@@ -444,29 +517,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mDelegate.onInMoveModeChanged();
}
- if (mCurrentMenuMode == MODE_NO_MENU) {
+ if (!isMenuOpen()) {
mDelegate.onMenuClosed();
}
}
- @VisibleForTesting
- String getMenuModeString() {
- return getMenuModeString(mCurrentMenuMode);
- }
-
- static String getMenuModeString(@TvPipMenuMode int menuMode) {
- switch(menuMode) {
- case MODE_NO_MENU:
- return "MODE_NO_MENU";
- case MODE_MOVE_MENU:
- return "MODE_MOVE_MENU";
- case MODE_ALL_ACTIONS_MENU:
- return "MODE_ALL_ACTIONS_MENU";
- default:
- return "Unknown";
- }
- }
-
// Start {@link TvPipMenuView.Delegate} methods
@Override
@@ -476,42 +531,19 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
- public void onBackPress() {
- if (!onExitMoveMode()) {
- closeMenu();
- }
- }
-
- @Override
- public boolean onExitMoveMode() {
+ public void onExitCurrentMenuMode() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExitMoveMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
-
- final int saveMenuMode = mCurrentMenuMode;
- if (isInMoveMode()) {
- switchToMenuMode(mPrevMenuMode);
- }
- return saveMenuMode == MODE_MOVE_MENU;
+ "%s: onExitCurrentMenuMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
+ requestMenuMode(isInMoveMode() ? mPrevMenuMode : MODE_NO_MENU);
}
@Override
- public boolean onPipMovement(int keycode) {
+ public void onPipMovement(int keycode) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onPipMovement - mCurrentMenuMode=%s", TAG, getMenuModeString());
if (isInMoveMode()) {
mDelegate.movePip(keycode);
}
- return isInMoveMode();
- }
-
- @Override
- public void onPipWindowFocusChanged(boolean focused) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
- mMenuIsFocused = focused;
- if (!focused && isMenuOpen()) {
- closeMenu();
- }
}
interface Delegate {
@@ -524,21 +556,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void closeEduText();
}
- private void grantPipMenuFocus(boolean grantFocus) {
- if (mMenuIsFocused == grantFocus) return;
-
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: grantWindowFocus(%b)", TAG, grantFocus);
-
- try {
- WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
- mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus);
- } catch (Exception e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Unable to update focus, %s", TAG, e);
- }
- }
-
private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback {
private final View mView;
private final int mZOrder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 45e1cde8f9a9..57439a59ccca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -328,7 +328,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
return menuUiBounds;
}
- void transitionToMenuMode(int menuMode, boolean resetMenu) {
+ void transitionToMenuMode(int menuMode) {
switch (menuMode) {
case MODE_NO_MENU:
hideAllUserControls();
@@ -337,7 +337,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
showMoveMenu();
break;
case MODE_ALL_ACTIONS_MENU:
- showAllActionsMenu(resetMenu);
+ showAllActionsMenu();
break;
default:
throw new IllegalArgumentException(
@@ -362,13 +362,13 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
mEduTextDrawer.closeIfNeeded();
}
- private void showAllActionsMenu(boolean resetMenu) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showAllActionsMenu(), resetMenu %b", TAG, resetMenu);
+ void resetMenu() {
+ scrollToFirstAction();
+ }
- if (resetMenu) {
- scrollToFirstAction();
- }
+ private void showAllActionsMenu() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showAllActionsMenu()", TAG);
if (mCurrentMenuMode == MODE_ALL_ACTIONS_MENU) return;
@@ -431,12 +431,6 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
}
}
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- mListener.onPipWindowFocusChanged(hasWindowFocus);
- }
-
private void animateAlphaTo(float alpha, View view) {
if (view.getAlpha() == alpha) {
return;
@@ -483,28 +477,28 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
if (event.getAction() == ACTION_UP) {
if (event.getKeyCode() == KEYCODE_BACK) {
- mListener.onBackPress();
+ mListener.onExitCurrentMenuMode();
return true;
}
- if (mA11yManager.isEnabled()) {
- return super.dispatchKeyEvent(event);
- }
-
- switch (event.getKeyCode()) {
- case KEYCODE_DPAD_UP:
- case KEYCODE_DPAD_DOWN:
- case KEYCODE_DPAD_LEFT:
- case KEYCODE_DPAD_RIGHT:
- return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
- event);
- case KEYCODE_ENTER:
- case KEYCODE_DPAD_CENTER:
- return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
- default:
- break;
+ if (mCurrentMenuMode == MODE_MOVE_MENU && !mA11yManager.isEnabled()) {
+ switch (event.getKeyCode()) {
+ case KEYCODE_DPAD_UP:
+ case KEYCODE_DPAD_DOWN:
+ case KEYCODE_DPAD_LEFT:
+ case KEYCODE_DPAD_RIGHT:
+ mListener.onPipMovement(event.getKeyCode());
+ return true;
+ case KEYCODE_ENTER:
+ case KEYCODE_DPAD_CENTER:
+ mListener.onExitCurrentMenuMode();
+ return true;
+ default:
+ // Dispatch key event as normal below
+ }
}
}
+
return super.dispatchKeyEvent(event);
}
@@ -529,7 +523,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
if (a11yEnabled) {
mA11yDoneButton.setVisibility(VISIBLE);
mA11yDoneButton.setOnClickListener(v -> {
- mListener.onExitMoveMode();
+ mListener.onExitCurrentMenuMode();
});
mA11yDoneButton.requestFocus();
mA11yDoneButton.requestAccessibilityFocus();
@@ -626,26 +620,15 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L
interface Listener {
- void onBackPress();
-
- /**
- * Called when a button for exiting move mode was pressed.
- *
- * @return true if the event was handled or false if the key event should be handled by the
- * next receiver.
- */
- boolean onExitMoveMode();
-
/**
- * @return whether pip movement was handled.
+ * Called when a button for exiting the current menu mode was pressed.
*/
- boolean onPipMovement(int keycode);
+ void onExitCurrentMenuMode();
/**
- * Called when the TvPipMenuView loses focus. This also means that the TV PiP menu window
- * has lost focus.
+ * Called when a button to move the PiP in a certain direction, indicated by keycode.
*/
- void onPipWindowFocusChanged(boolean focused);
+ void onPipMovement(int keycode);
/**
* The edu text closing impacts the size of the Picture-in-Picture window and influences
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
index ec09827fa4d1..6dabb3bf6f9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -1,3 +1,4 @@
# WM shell sub-module pip owner
hwwang@google.com
mateuszc@google.com
+gabiyev@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 069066e4bd49..2616b8b08bf1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -34,4 +34,10 @@ public interface RecentTasks {
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
Consumer<List<GroupedRecentTaskInfo>> callback) {
}
+
+ /**
+ * Adds the listener to be notified of whether the recent task animation is running.
+ */
+ default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index f35eda6caef0..ccc34389557c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -203,6 +203,17 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
}
+ @Nullable
+ public SplitBounds getSplitBoundsForTaskId(int taskId) {
+ if (taskId == INVALID_TASK_ID) {
+ return null;
+ }
+
+ // We could do extra verification of requiring both taskIds of a pair and verifying that
+ // the same split bounds object is returned... but meh. Seems unnecessary.
+ return mTaskSplitBoundsMap.get(taskId);
+ }
+
@Override
public Context getContext() {
return mContext;
@@ -329,7 +340,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
continue;
}
- if (DesktopModeStatus.isProto2Enabled() && mDesktopModeTaskRepository.isPresent()
+ if (DesktopModeStatus.isEnabled() && mDesktopModeTaskRepository.isPresent()
&& mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
freeformTasks.add(taskInfo);
@@ -417,6 +428,21 @@ public class RecentTasksController implements TaskStackListenerCallback,
executor.execute(() -> callback.accept(tasks));
});
}
+
+ @Override
+ public void addAnimationStateListener(Executor executor, Consumer<Boolean> listener) {
+ mMainExecutor.execute(() -> {
+ if (mTransitionHandler == null) {
+ return;
+ }
+ mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onAnimationStateChanged(boolean running) {
+ executor.execute(() -> listener.accept(running));
+ }
+ });
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 50ba8975802b..ead2f9cbd1ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.recents;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -23,6 +24,8 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -69,8 +72,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
private final Transitions mTransitions;
private final ShellExecutor mExecutor;
+ @Nullable
+ private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
private final ArrayList<RecentsController> mControllers = new ArrayList<>();
+ private final ArrayList<RecentsTransitionStateListener> mStateListeners = new ArrayList<>();
/**
* List of other handlers which might need to mix recents with other things. These are checked
@@ -82,6 +88,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
@Nullable RecentTasksController recentTasksController) {
mTransitions = transitions;
mExecutor = transitions.getMainExecutor();
+ mRecentTasksController = recentTasksController;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
if (recentTasksController == null) return;
shellInit.addInitCallback(() -> {
@@ -100,6 +107,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
mMixers.remove(mixer);
}
+ /** Adds the callback for receiving the state change of transition. */
+ public void addTransitionStateListener(RecentsTransitionStateListener listener) {
+ mStateListeners.add(listener);
+ }
+
@VisibleForTesting
public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -122,6 +134,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
mixedHandler == null ? this : mixedHandler);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onTransitionStarted(transition);
+ }
if (mixer != null) {
mixer.setRecentsTransition(transition);
}
@@ -165,13 +180,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
return false;
}
final RecentsController controller = mControllers.get(controllerIdx);
- Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
+ final IApplicationThread animApp = mAnimApp;
mAnimApp = null;
if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"RecentsTransitionHandler.startAnimation: failed to start animation");
return false;
}
+ Transitions.setRunningRemoteTransitionDelegate(animApp);
return true;
}
@@ -382,6 +398,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
mTransition = null;
mPendingPauseSnapshotsForCancel = null;
mControllers.remove(this);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(false);
+ }
}
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
@@ -426,6 +445,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
mLeashMap = new ArrayMap<>();
mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
+ int closingSplitTaskId = INVALID_TASK_ID;
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
@@ -452,6 +472,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
apps.add(target);
if (TransitionUtil.isClosingType(change.getMode())) {
mPausingTasks.add(new TaskState(change, target.leash));
+ closingSplitTaskId = change.getTaskInfo().taskId;
if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" adding pausing leaf home taskId=%d", taskInfo.taskId);
@@ -509,13 +530,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
}
t.apply();
+ Bundle b = new Bundle(1 /*capacity*/);
+ b.putParcelable(KEY_EXTRA_SPLIT_BOUNDS,
+ mRecentTasksController.getSplitBoundsForTaskId(closingSplitTaskId));
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.start: calling onAnimationStart", mInstanceId);
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
- new Rect(0, 0, 0, 0), new Rect());
+ new Rect(0, 0, 0, 0), new Rect(), b);
+ for (int i = 0; i < mStateListeners.size(); i++) {
+ mStateListeners.get(i).onAnimationStateChanged(true);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error starting recents animation", e);
cancel("onAnimationStart() failed");
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
index a401cb494822..e8733ebd8f03 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.bubble
+package com.android.wm.shell.recents;
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import android.os.IBinder;
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) :
- SendBubbleNotificationTest(flicker)
+/** The listener for the events from {@link RecentsTransitionHandler}. */
+public interface RecentsTransitionStateListener {
+
+ /** Notifies whether the recents animation is running. */
+ default void onAnimationStateChanged(boolean running) {
+ }
+
+ /** Notifies that a recents shell transition has started. */
+ default void onTransitionStarted(IBinder transition) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 14304a3c0aac..253acc49071a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -91,42 +91,42 @@ interface ISplitScreen {
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int taskId1, in Bundle options1, int taskId2, in Bundle options2,
- int splitPosition, float splitRatio, in RemoteTransition remoteTransition,
+ int splitPosition, int snapPosition, in RemoteTransition remoteTransition,
in InstanceId instanceId) = 10;
/**
* Starts a pair of intent and task in one transition.
*/
oneway void startIntentAndTask(in PendingIntent pendingIntent, int userId1, in Bundle options1,
- int taskId, in Bundle options2, int sidePosition, float splitRatio,
+ int taskId, in Bundle options2, int sidePosition, int snapPosition,
in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
/**
* Starts a pair of shortcut and task in one transition.
*/
oneway void startShortcutAndTask(in ShortcutInfo shortcutInfo, in Bundle options1, int taskId,
- in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options2, int splitPosition, int snapPosition,
in RemoteTransition remoteTransition, in InstanceId instanceId) = 17;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int taskId1, in Bundle options1, int taskId2,
- in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
/**
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, int userId1,
- in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
/**
* Starts a pair of shortcut and task using legacy transition system.
*/
oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo,
- in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+ in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition,
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
/**
@@ -135,7 +135,7 @@ interface ISplitScreen {
oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1, int userId1,
in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
- float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
+ int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
/**
* Start a pair of intents in one transition.
@@ -143,7 +143,7 @@ interface ISplitScreen {
oneway void startIntents(in PendingIntent pendingIntent1, int userId1,
in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition,
- float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
+ int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index f90ee586e696..ccffa02a22c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,7 +23,6 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -43,6 +42,7 @@ import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.TaskInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -85,6 +85,7 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -130,6 +131,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
public static final int EXIT_REASON_RECREATE_SPLIT = 10;
public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
+ public static final int EXIT_REASON_ENTER_DESKTOP = 12;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -143,6 +145,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
EXIT_REASON_CHILD_TASK_ENTER_PIP,
EXIT_REASON_RECREATE_SPLIT,
EXIT_REASON_FULLSCREEN_SHORTCUT,
+ EXIT_REASON_ENTER_DESKTOP
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -598,8 +601,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (options1 == null) options1 = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
@@ -623,13 +626,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
- activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
+ activityOptions.toBundle(), taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
if (options1 == null) options1 = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
final String packageName1 = shortcutInfo.getPackage();
@@ -656,7 +660,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
mStageCoordinator.startShortcutAndTask(shortcutInfo, activityOptions.toBundle(), taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId);
+ options2, splitPosition, snapPosition, remoteTransition, instanceId);
}
/**
@@ -671,8 +675,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
Intent fillInIntent = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
@@ -693,12 +697,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
- options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+ options1, taskId, options2, splitPosition, snapPosition, adapter, instanceId);
}
private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
@@ -725,14 +729,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId);
+ options2, splitPosition, snapPosition, remoteTransition, instanceId);
}
private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -756,14 +760,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
- splitPosition, splitRatio, adapter, instanceId);
+ splitPosition, snapPosition, adapter, instanceId);
}
private void startIntents(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
@@ -798,7 +803,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
- activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ activityOptions2.toBundle(), splitPosition, snapPosition, remoteTransition,
instanceId);
}
@@ -814,21 +819,22 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final String packageName1 = SplitScreenUtils.getPackageName(intent);
final String packageName2 = getPackageName(reverseSplitPosition(position));
final int userId2 = getUserId(reverseSplitPosition(position));
+ final ComponentName component = intent.getIntent().getComponent();
+
+ // To prevent accumulating large number of instances in the background, reuse task
+ // in the background. If we don't explicitly reuse, new may be created even if the app
+ // isn't multi-instance because WM won't automatically remove/reuse the previous instance
+ final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
+ .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1))
+ .orElse(null);
+ if (taskInfo != null) {
+ startTask(taskInfo.taskId, position, options);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Start task in background");
+ return;
+ }
if (samePackage(packageName1, packageName2, userId1, userId2)) {
if (supportMultiInstancesSplit(packageName1)) {
- // To prevent accumulating large number of instances in the background, reuse task
- // in the background with priority.
- final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
- .map(recentTasks -> recentTasks.findTaskInBackground(
- intent.getIntent().getComponent(), userId1))
- .orElse(null);
- if (taskInfo != null) {
- startTask(taskInfo.taskId, position, options);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Start task in background");
- return;
- }
-
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
// the split and there is no reusable background task.
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -1009,6 +1015,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return "CHILD_TASK_ENTER_PIP";
case EXIT_REASON_RECREATE_SPLIT:
return "RECREATE_SPLIT";
+ case EXIT_REASON_ENTER_DESKTOP:
+ return "ENTER_DESKTOP";
default:
return "unknown reason, reason int = " + exitReason;
}
@@ -1217,78 +1225,82 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
- taskId1, options1, taskId2, options2, splitPosition,
- splitRatio, adapter, instanceId));
+ taskId1, options1, taskId2, options2, splitPosition, snapPosition,
+ adapter, instanceId));
}
@Override
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
- Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ Bundle options1, int taskId, Bundle options2, int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startIntentAndTaskWithLegacyTransition", (controller) ->
controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
- userId1, options1, taskId, options2, splitPosition, splitRatio,
- adapter, instanceId));
+ userId1, options1, taskId, options2, splitPosition,
+ snapPosition, adapter, instanceId));
}
@Override
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, options1, taskId, options2, splitPosition,
- splitRatio, adapter, instanceId));
+ snapPosition, adapter, instanceId));
}
@Override
public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
- taskId2, options2, splitPosition, splitRatio, remoteTransition,
+ taskId2, options2, splitPosition, snapPosition, remoteTransition,
instanceId));
}
@Override
public void startIntentAndTask(PendingIntent pendingIntent, int userId1,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
(controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1,
- taskId, options2, splitPosition, splitRatio, remoteTransition,
+ taskId, options2, splitPosition, snapPosition, remoteTransition,
instanceId));
}
@Override
public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
(controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
- options2, splitPosition, splitRatio, remoteTransition, instanceId));
+ options2, splitPosition, snapPosition, remoteTransition, instanceId));
}
@Override
public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
(controller) ->
controller.startIntentsWithLegacyTransition(pendingIntent1, userId1,
shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2,
- options2, splitPosition, splitRatio, adapter, instanceId)
+ options2, splitPosition, snapPosition, adapter, instanceId)
);
}
@@ -1296,13 +1308,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startIntents(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @Nullable Bundle options2, @SplitPosition int splitPosition,
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntents",
(controller) ->
controller.startIntents(pendingIntent1, userId1, shortcutInfo1,
options1, pendingIntent2, userId2, shortcutInfo2, options2,
- splitPosition, splitRatio, remoteTransition, instanceId)
+ splitPosition, snapPosition, remoteTransition, instanceId)
);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 5483fa5d29f6..f4ab2266179a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,6 +25,7 @@ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED_
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -42,6 +43,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -192,6 +194,8 @@ public class SplitscreenEventLogger {
return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
case EXIT_REASON_FULLSCREEN_SHORTCUT:
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
+ case EXIT_REASON_ENTER_DESKTOP:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
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 8efdfd6ca1e1..3d825f072a6a 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
@@ -128,6 +128,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitWindowManager;
@@ -631,8 +632,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
/** Starts 2 tasks in one transition. */
- void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
- @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+ void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId2 == INVALID_TASK_ID) {
@@ -654,13 +655,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.startTask(taskId1, options1);
- startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
}
/** Start an intent and a task to a split pair in one transition. */
void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
@@ -676,13 +677,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
- startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
/** Starts a shortcut and a task to a split pair in one transition. */
void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ @SnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -697,7 +699,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
- startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId);
+ startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
/**
@@ -708,14 +710,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
*/
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
- @Nullable Bundle mainOptions, float splitRatio,
+ @Nullable Bundle mainOptions, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
if (!mMainStage.isActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(wct, false /* reparent */);
}
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
setRootForceTranslucent(false, wct);
@@ -740,7 +742,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
PendingIntent pendingIntent2, Intent fillInIntent2,
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (pendingIntent2 == null) {
@@ -762,7 +764,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
setSideStagePosition(splitPosition, wct);
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
setRootForceTranslucent(false, wct);
@@ -790,7 +792,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Starts a pair of tasks using legacy transition. */
void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
- float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId2 == INVALID_TASK_ID) {
@@ -811,7 +813,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.startTask(taskId1, options1);
mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition);
- startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId2, options2, splitPosition, snapPosition, adapter,
instanceId);
}
@@ -820,8 +822,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
@Nullable PendingIntent pendingIntent2, Intent fillInIntent2,
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (pendingIntent2 == null) {
@@ -840,13 +842,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition);
}
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
- splitPosition, splitRatio, adapter, instanceId);
+ splitPosition, snapPosition, adapter, instanceId);
}
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
@@ -859,15 +861,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition);
- startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
/** Starts a pair of shortcut and task using legacy transition. */
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int splitPosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
@@ -878,7 +880,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
- startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
+ startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter,
instanceId);
}
@@ -928,18 +930,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void startWithLegacyTransition(WindowContainerTransaction wct,
@Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
@Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent,
- mainShortcutInfo, mainOptions, sidePosition, splitRatio, adapter, instanceId);
+ mainShortcutInfo, mainOptions, sidePosition, snapPosition, adapter, instanceId);
}
private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
- @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter, InstanceId instanceId) {
+ @Nullable Bundle mainOptions, @SplitPosition int sidePosition,
+ @SnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */,
null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition,
- splitRatio, adapter, instanceId);
+ snapPosition, adapter, instanceId);
}
/**
@@ -950,15 +953,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
@Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
@Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ @SplitPosition int sidePosition, @SnapPosition int snapPosition,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (!isSplitScreenVisible()) {
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
}
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
- mSplitLayout.setDivideRatio(splitRatio);
+ mSplitLayout.setDivideRatio(snapPosition);
// Apply surface bounds before animation start.
SurfaceControl.Transaction startT = mTransactionPool.acquire();
@@ -1766,7 +1769,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
rightBottomTaskId = sideStageTopTaskId;
}
SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
- leftTopTaskId, rightBottomTaskId);
+ leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 20da8773f387..edb5aba1e46b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -46,6 +46,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private final int mIconFadeOutDuration;
private final int mAppRevealDelay;
private final int mAppRevealDuration;
+ @SplashScreenExitAnimationUtils.ExitAnimationType
+ private final int mAnimationType;
private final int mAnimationDuration;
private final float mIconStartAlpha;
private final float mBrandingStartAlpha;
@@ -91,6 +93,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
}
mAppRevealDuration = context.getResources().getInteger(
R.integer.starting_window_app_reveal_anim_duration);
+ mAnimationType = context.getResources().getInteger(
+ R.integer.starting_window_exit_animation_type);
mAnimationDuration = Math.max(mIconFadeOutDuration, mAppRevealDelay + mAppRevealDuration);
mMainWindowShiftLength = mainWindowShiftLength;
mFinishCallback = handleFinish;
@@ -98,10 +102,10 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
}
void startAnimations() {
- SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface,
- mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration,
- mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay,
- mAppRevealDuration, this, mRoundedCornerRadius);
+ SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView,
+ mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame,
+ mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha,
+ mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius);
}
private void reset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
index a7e4385b60c8..e86b62dee86d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
@@ -20,6 +20,7 @@ import static android.view.Choreographer.CALLBACK_COMMIT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
@@ -54,6 +55,7 @@ import com.android.wm.shell.common.TransactionPool;
public class SplashScreenExitAnimationUtils {
private static final boolean DEBUG_EXIT_ANIMATION = false;
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+ private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
private static final String TAG = "SplashScreenExitAnimationUtils";
private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
@@ -62,20 +64,47 @@ public class SplashScreenExitAnimationUtils {
private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
/**
+ * This splash screen exit animation type uses a radial vanish to hide
+ * the starting window and slides up the main window content.
+ * @hide
+ */
+ public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
+
+ /**
+ * This splash screen exit animation type fades out the starting window
+ * to reveal the main window content.
+ * @hide
+ */
+ public static final int TYPE_FADE_OUT = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_RADIAL_VANISH_SLIDE_UP,
+ TYPE_FADE_OUT,
+ })
+ public @interface ExitAnimationType {}
+
+ /**
* Creates and starts the animator to fade out the icon, reveal the app, and shift up main
* window with rounded corner radius.
*/
- static void startAnimations(ViewGroup splashScreenView,
- SurfaceControl firstWindowSurface, int mainWindowShiftLength,
- TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
- int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
- int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
- float roundedCornerRadius) {
- ValueAnimator animator =
- createAnimator(splashScreenView, firstWindowSurface, mainWindowShiftLength,
- transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
- iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
- animatorListener, roundedCornerRadius);
+ static void startAnimations(@ExitAnimationType int animationType,
+ ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
+ int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
+ ValueAnimator animator;
+ if (animationType == TYPE_FADE_OUT) {
+ animator = createFadeOutAnimation(splashScreenView, animationDuration,
+ iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
+ appRevealDuration, animatorListener);
+ } else {
+ animator = createRadialVanishSlideUpAnimator(splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+ }
animator.start();
}
@@ -89,17 +118,18 @@ public class SplashScreenExitAnimationUtils {
TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
- startAnimations(splashScreenView, firstWindowSurface, mainWindowShiftLength,
- transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration,
- iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration,
- animatorListener, 0f /* roundedCornerRadius */);
+ // Start the default 'reveal' animation.
+ startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
+ firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+ animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+ appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
}
/**
* Creates the animator to fade out the icon, reveal the app, and shift up main window.
* @hide
*/
- private static ValueAnimator createAnimator(ViewGroup splashScreenView,
+ private static ValueAnimator createRadialVanishSlideUpAnimator(ViewGroup splashScreenView,
SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
@@ -210,6 +240,59 @@ public class SplashScreenExitAnimationUtils {
return nightMode == Configuration.UI_MODE_NIGHT_YES;
}
+ private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
+ int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+ float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+ Animator.AnimatorListener animatorListener) {
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ splashScreenView.setBackgroundColor(Color.BLUE);
+ }
+
+ final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ animator.setDuration(animationDuration);
+ animator.setInterpolator(Interpolators.LINEAR);
+ animator.addUpdateListener(animation -> {
+
+ float linearProgress = (float) animation.getAnimatedValue();
+
+ // Icon fade out progress (always starts immediately)
+ final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
+ linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+ View iconView = null;
+ View brandingView = null;
+
+ if (splashScreenView instanceof SplashScreenView) {
+ iconView = ((SplashScreenView) splashScreenView).getIconView();
+ brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+ }
+ if (iconView != null) {
+ iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
+ }
+ if (brandingView != null) {
+ brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
+ }
+
+ // Splash screen fade out progress (possibly delayed)
+ final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
+ getProgress(linearProgress, appRevealDelay,
+ appRevealDuration, animationDuration));
+
+ splashScreenView.setAlpha(1f - splashFadeProgress);
+
+ if (DEBUG_EXIT_FADE_ANIMATION) {
+ Slog.d(TAG, "progress -> animation: " + linearProgress
+ + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
+ + "\t splash alpha: " + splashScreenView.getAlpha()
+ );
+ }
+ });
+ if (animatorListener != null) {
+ animator.addListener(animatorListener);
+ }
+ return animator;
+ }
+
/**
* View which creates a circular reveal of the underlying view.
* @hide
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 29be34347b22..0c6adc942385 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -112,28 +112,15 @@ public class SplashscreenContentDrawer {
*/
static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
- // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
- // icon which it's non-transparent foreground area is similar to it's background area, then
- // do not enlarge the foreground drawable.
- // For example, an icon with the foreground 108*108 opaque pixels and it's background
- // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
- private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
-
- /**
- * If the developer doesn't specify a background for the icon, we slightly scale it up.
- *
- * The background is either manually specified in the theme or the Adaptive Icon
- * background is used if it's different from the window background.
- */
- private static final float NO_BACKGROUND_SCALE = 192f / 160;
private final Context mContext;
private final HighResIconProvider mHighResIconProvider;
-
private int mIconSize;
private int mDefaultIconSize;
private int mBrandingImageWidth;
private int mBrandingImageHeight;
private int mMainWindowShiftLength;
+ private float mEnlargeForegroundIconThreshold;
+ private float mNoBackgroundScale;
private int mLastPackageContextConfigHash;
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
@@ -336,6 +323,10 @@ public class SplashscreenContentDrawer {
com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
+ mEnlargeForegroundIconThreshold = mContext.getResources().getFloat(
+ com.android.wm.shell.R.dimen.splash_icon_enlarge_foreground_threshold);
+ mNoBackgroundScale = mContext.getResources().getFloat(
+ com.android.wm.shell.R.dimen.splash_icon_no_background_scale_factor);
}
/**
@@ -604,14 +595,14 @@ public class SplashscreenContentDrawer {
// There is no background below the icon, so scale the icon up
if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT
|| mTmpAttrs.mIconBgColor == mThemeColor) {
- mFinalIconSize *= NO_BACKGROUND_SCALE;
+ mFinalIconSize *= mNoBackgroundScale;
}
createIconDrawable(iconDrawable, false /* legacy */, false /* loadInDetail */);
} else {
final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
final int densityDpi = mContext.getResources().getConfiguration().densityDpi;
final int scaledIconDpi =
- (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
+ (int) (0.5f + iconScale * densityDpi * mNoBackgroundScale);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon");
iconDrawable = mHighResIconProvider.getIcon(
mActivityInfo, densityDpi, scaledIconDpi);
@@ -693,8 +684,8 @@ public class SplashscreenContentDrawer {
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
// scale by 192/160 if we only draw adaptiveIcon's foreground.
final float noBgScale =
- iconColor.mFgNonTranslucentRatio < ENLARGE_FOREGROUND_ICON_THRESHOLD
- ? NO_BACKGROUND_SCALE : 1f;
+ iconColor.mFgNonTranslucentRatio < mEnlargeForegroundIconThreshold
+ ? mNoBackgroundScale : 1f;
// Using AdaptiveIconDrawable here can help keep the shape consistent with the
// current settings.
mFinalIconSize = (int) (0.5f + mIconSize * noBgScale);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 72fc8686f648..6ea6516a96f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCR
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -52,8 +52,7 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- final boolean isSolidColorSplashScreen =
- (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
+ final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -67,13 +66,13 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
+ "processRunning=%b, "
+ "allowTaskSnapshot=%b, "
+ "activityCreated=%b, "
- + "isSolidColorSplashScreen=%b, "
+ + "allowIcon=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
+ "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
+ allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
topIsHome);
if (windowlessSurface) {
@@ -81,7 +80,7 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
}
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
@@ -95,18 +94,18 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
}
}
if (!activityDrawn && !topIsHome) {
- return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
+ return getSplashscreenType(allowIcon, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
- private static int getSplashscreenType(boolean solidColorSplashScreen,
- boolean legacySplashScreen) {
- return solidColorSplashScreen
- ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
- : legacySplashScreen
- ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
+ if (allowIcon) {
+ return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
: STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index cefbb17d783d..e03f82526bdb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -165,19 +165,6 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
return null;
}
- /**
- * Returns all the pending transitions for a given `taskView`.
- * @param taskView the pending transition should be for this.
- */
- ArrayList<PendingTransition> findAllPending(TaskViewTaskController taskView) {
- ArrayList<PendingTransition> list = new ArrayList<>();
- for (int i = mPending.size() - 1; i >= 0; --i) {
- if (mPending.get(i).mTaskView != taskView) continue;
- list.add(mPending.get(i));
- }
- return list;
- }
-
private PendingTransition findPending(IBinder claimed) {
for (int i = 0; i < mPending.size(); ++i) {
if (mPending.get(i).mClaimed != claimed) continue;
@@ -273,10 +260,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
// Task view isn't visible, the bounds will next visibility update.
return;
}
- PendingTransition pendingOpen = findPendingOpeningTransition(taskView);
- if (pendingOpen != null) {
- // There is already an opening transition in-flight, the window bounds will be
- // set in prepareOpenAnimation (via the window crop) if needed.
+ if (hasPending()) {
+ // There is already a transition in-flight, the window bounds will be set in
+ // prepareOpenAnimation.
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 32cc6f8cebc0..83dc7fa5e869 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -19,10 +19,10 @@ package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -43,7 +43,6 @@ import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.split.SplitScreenUtils;
-import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
@@ -71,7 +70,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
private RecentsTransitionHandler mRecentsHandler;
private StageCoordinator mSplitHandler;
private final KeyguardTransitionHandler mKeyguardHandler;
- private DesktopModeController mDesktopModeController;
private DesktopTasksController mDesktopTasksController;
private UnfoldTransitionHandler mUnfoldHandler;
@@ -149,7 +147,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
@Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
- Optional<DesktopModeController> desktopModeControllerOptional,
Optional<DesktopTasksController> desktopTasksControllerOptional,
Optional<UnfoldTransitionHandler> unfoldHandler) {
mPlayer = player;
@@ -169,7 +166,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
if (mRecentsHandler != null) {
mRecentsHandler.addMixer(this);
}
- mDesktopModeController = desktopModeControllerOptional.orElse(null);
mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
}, this);
@@ -255,9 +251,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
@Override
public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
- if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible()
- || DesktopModeStatus.isActive(mPlayer.getContext()))) {
- return this;
+ if (mRecentsHandler != null) {
+ if (mSplitHandler.isSplitScreenVisible()) {
+ return this;
+ } else if (mDesktopTasksController != null
+ // Check on the default display. Recents/gesture nav is only available there
+ && mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) {
+ return this;
+ }
}
return null;
}
@@ -271,7 +272,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
mixed.mLeftoversHandler = mRecentsHandler;
mActiveTransitions.add(mixed);
- } else if (DesktopModeStatus.isActive(mPlayer.getContext())) {
+ } else if (DesktopModeStatus.isEnabled()) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "desktop mode is active, so treat it as Mixed.");
final MixedTransition mixed = new MixedTransition(
@@ -697,12 +698,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
if (!consumed) {
return false;
}
- //Sync desktop mode state (proto 1)
- if (mDesktopModeController != null) {
- mDesktopModeController.syncSurfaceState(info, finishTransaction);
- return true;
- }
- //Sync desktop mode state (proto 2)
if (mDesktopTasksController != null) {
mDesktopTasksController.syncSurfaceState(info, finishTransaction);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index bbf67a6155d7..a90edf20f94e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -137,7 +137,6 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
});
}
};
- Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
try {
// If the remote is actually in the same process, then make a copy of parameters since
// remote impls assume that they have to clean-up native references.
@@ -149,6 +148,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();
+ Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
unhandleDeath(remote.asBinder(), finishCallback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index d978eafa97f3..d07d2b7b6db9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -346,7 +346,7 @@ public class TransitionAnimationHelper {
.setFrameScale(1)
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
- .setAllowProtected(true)
+ .setAllowProtected(false)
.setCaptureSecureLayers(true)
.build();
final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
index f209521b1da4..a68b41d6563a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
@@ -19,6 +19,8 @@ import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
+
import java.util.Objects;
/**
@@ -26,6 +28,8 @@ import java.util.Objects;
* tasks/leashes/etc in Launcher
*/
public class SplitBounds implements Parcelable {
+ public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds";
+
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
/** This rect represents the actual gap between the two apps */
@@ -35,6 +39,7 @@ public class SplitBounds implements Parcelable {
public final float leftTaskPercent;
public final float dividerWidthPercent;
public final float dividerHeightPercent;
+ public final @SnapPosition int snapPosition;
/**
* If {@code true}, that means at the time of creation of this object, the
* split-screened apps were vertically stacked. This is useful in scenarios like
@@ -45,12 +50,13 @@ public class SplitBounds implements Parcelable {
public final int leftTopTaskId;
public final int rightBottomTaskId;
- public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
- int leftTopTaskId, int rightBottomTaskId) {
+ public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+ int rightBottomTaskId, @SnapPosition int snapPosition) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
this.leftTopTaskId = leftTopTaskId;
this.rightBottomTaskId = rightBottomTaskId;
+ this.snapPosition = snapPosition;
if (rightBottomBounds.top > leftTopBounds.top) {
// vertical apps, horizontal divider
@@ -81,8 +87,9 @@ public class SplitBounds implements Parcelable {
appsStackedVertically = parcel.readBoolean();
leftTopTaskId = parcel.readInt();
rightBottomTaskId = parcel.readInt();
- dividerWidthPercent = parcel.readInt();
- dividerHeightPercent = parcel.readInt();
+ dividerWidthPercent = parcel.readFloat();
+ dividerHeightPercent = parcel.readFloat();
+ snapPosition = parcel.readInt();
}
@Override
@@ -97,6 +104,7 @@ public class SplitBounds implements Parcelable {
parcel.writeInt(rightBottomTaskId);
parcel.writeFloat(dividerWidthPercent);
parcel.writeFloat(dividerHeightPercent);
+ parcel.writeInt(snapPosition);
}
@Override
@@ -127,7 +135,8 @@ public class SplitBounds implements Parcelable {
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
+ "Divider: " + visualDividerBounds + "\n"
- + "AppsVertical? " + appsStackedVertically;
+ + "AppsVertical? " + appsStackedVertically + "\n"
+ + "snapPosition: " + snapPosition;
}
public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
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 ce8191067ae9..82fc0f49c143 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
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -64,7 +65,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
Handler handler,
Choreographer choreographer,
SyncTransactionQueue syncQueue) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface);
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface,
+ taskInfo.getConfiguration());
mHandler = handler;
mChoreographer = choreographer;
@@ -113,7 +115,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -226,7 +228,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
}
@Override
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return R.dimen.freeform_decor_caption_height;
}
}
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 3a0bdd668fbc..afa2754803f1 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
@@ -64,21 +64,25 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener;
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
import java.util.Optional;
import java.util.function.Supplier;
@@ -100,13 +104,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
- private final Optional<DesktopModeController> mDesktopModeController;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final RecentsTransitionHandler mRecentsTransitionHandler;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
- private final TaskCornersListener mCornersListener = new TaskCornersListenerImpl();
+ private final ExclusionRegionListener mExclusionRegionListener =
+ new ExclusionRegionListenerImpl();
private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
new SparseArray<>();
@@ -120,7 +125,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private MoveToDesktopAnimator mMoveToDesktopAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
- private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
+ private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
+ new DesktopModeKeyguardChangeListener();
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -132,8 +139,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController
+ Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
) {
this(
context,
@@ -145,12 +153,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
shellController,
syncQueue,
transitions,
- desktopModeController,
desktopTasksController,
+ recentsTransitionHandler,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- new DesktopModeKeyguardChangeListener());
+ rootTaskDisplayAreaOrganizer);
}
@VisibleForTesting
@@ -164,12 +172,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
+ RecentsTransitionHandler recentsTransitionHandler,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -179,19 +187,25 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController;
mSyncQueue = syncQueue;
mTransitions = transitions;
- mDesktopModeController = desktopModeController;
mDesktopTasksController = desktopTasksController;
+ mRecentsTransitionHandler = recentsTransitionHandler;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
- mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
shellInit.addInitCallback(this::onInit, this);
}
private void onInit() {
mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
+ mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() {
+ @Override
+ public void onTransitionStarted(IBinder transition) {
+ onRecentsTransitionStarted(transition);
+ }
+ });
}
@Override
@@ -207,9 +221,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
if (visible) {
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
- if (decor != null && DesktopModeStatus.isActive(mContext)
+ if (decor != null && DesktopModeStatus.isEnabled()
&& decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo));
}
}
@@ -318,8 +331,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
+ private void onRecentsTransitionStarted(IBinder transition) {
+ // Block relayout on window decorations originating from #onTaskInfoChanges until the
+ // animation completes to avoid interfering with the transition animation.
+ for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
+ final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ decor.incrementRelayoutBlock();
+ decor.addTransitionPausingRelayout(transition);
+ }
+ }
+
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
- implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
+ implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
+ DragDetector.MotionEventHandler{
private final int mTaskId;
private final WindowContainerToken mTaskToken;
@@ -347,18 +371,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int id = v.getId();
if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken);
- if (mSplitScreenController != null
- && mSplitScreenController.isSplitScreenVisible()) {
- int remainingTaskPosition = mTaskId == mSplitScreenController
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController
- .getTaskInfo(remainingTaskPosition);
+ if (isTaskInSplitScreen(mTaskId)) {
+ RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
}
+ decoration.closeMaximizeMenu();
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
+ decoration.closeMaximizeMenu();
if (!decoration.isHandleMenuActive()) {
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
decoration.createHandleMenu();
@@ -366,7 +387,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
decoration.closeHandleMenu();
}
} else if (id == R.id.desktop_button) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
if (mDesktopTasksController.isPresent()) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// App sometimes draws before the insets from WindowDecoration#relayout have
@@ -374,10 +394,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
decoration.incrementRelayoutBlock();
mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct);
+ closeOtherSplitTask(mTaskId);
}
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
} else if (id == R.id.split_screen_button) {
@@ -392,13 +412,35 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
// TODO(b/278084491): dev option to enable display switching
// remove when select is implemented
mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
- decoration.closeHandleMenu();
}
} else if (id == R.id.maximize_window) {
+ moveTaskToFront(decoration.mTaskInfo);
+ if (decoration.isMaximizeMenuActive()) {
+ decoration.closeMaximizeMenu();
+ return;
+ }
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(
taskInfo, decoration));
decoration.closeHandleMenu();
+ } else if (id == R.id.maximize_menu_maximize_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId)));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ } else if (id == R.id.maximize_menu_snap_left_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.LEFT));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ } else if (id == R.id.maximize_menu_snap_right_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.RIGHT));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
}
}
@@ -413,10 +455,26 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return mDragDetector.onMotionEvent(v, e);
}
+ @Override
+ public boolean onLongClick(View v) {
+ final int id = v.getId();
+ if (id == R.id.maximize_window) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ moveTaskToFront(decoration.mTaskInfo);
+ if (decoration.isMaximizeMenuActive()) {
+ decoration.closeMaximizeMenu();
+ } else {
+ decoration.closeHandleMenu();
+ decoration.createMaximizeMenu();
+ }
+ return true;
+ }
+ return false;
+ }
+
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
- mDesktopModeController.ifPresent(c -> c.moveTaskToFront(taskInfo));
}
}
@@ -427,15 +485,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Override
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- if (DesktopModeStatus.isProto2Enabled()
+ if (DesktopModeStatus.isEnabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return false;
}
- if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
- && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
- == WINDOWING_MODE_FULLSCREEN) {
- return false;
- }
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
@@ -585,7 +638,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev);
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
if (relevantDecor == null
|| relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
|| mTransitionDragActive) {
@@ -594,14 +647,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
handleEventOutsideFocusedCaption(ev, relevantDecor);
// Prevent status bar from reacting to a caption drag.
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
if (mTransitionDragActive) {
inputMonitor.pilferPointers();
}
- } else if (DesktopModeStatus.isProto1Enabled()) {
- if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) {
- inputMonitor.pilferPointers();
- }
}
}
@@ -634,7 +683,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragToDesktopAnimationStartBounds.set(
relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds());
boolean dragFromStatusBarAllowed = false;
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
// In proto2 any full screen or multi-window task can be dragged to
// freeform.
final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode();
@@ -659,10 +708,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > 2 * statusBarHeight) {
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
- } else if (DesktopModeStatus.isProto1Enabled()) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
}
mMoveToDesktopAnimator = null;
return;
@@ -692,6 +739,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
+ closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
relevantDecor.mTaskSurface);
@@ -782,7 +830,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
// We can't look at focused task here as only one task will have focus.
- return getSplitScreenDecor(ev);
+ DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev);
+ return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor;
} else {
return getFocusedDecor();
}
@@ -853,7 +902,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
&& taskInfo.isFocused) {
return false;
}
- return DesktopModeStatus.isProto2Enabled()
+ return DesktopModeStatus.isEnabled()
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
@@ -880,7 +929,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
taskSurface,
mMainHandler,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mRootTaskDisplayAreaOrganizer);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
windowDecoration.createResizeVeil();
@@ -889,8 +939,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
- windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setCornersListener(mCornersListener);
+ windowDecoration.setCaptionListeners(
+ touchEventListener, touchEventListener, touchEventListener);
+ windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(dragPositioningCallback);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT,
@@ -912,11 +963,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
+ private RunningTaskInfo getOtherSplitTask(int taskId) {
+ @SplitPosition int remainingTaskPosition = mSplitScreenController
+ .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ return mSplitScreenController.getTaskInfo(remainingTaskPosition);
+ }
+
+ private void closeOtherSplitTask(int taskId) {
+ if (isTaskInSplitScreen(taskId)) {
+ mTaskOperations.closeTask(getOtherSplitTask(taskId).token);
+ }
+ }
+
+ private boolean isTaskInSplitScreen(int taskId) {
+ return mSplitScreenController != null
+ && mSplitScreenController.isTaskInSplitScreen(taskId);
+ }
+
private class DragStartListenerImpl
implements DragPositioningCallbackUtility.DragStartListener {
@Override
public void onDragStart(int taskId) {
- mWindowDecorByTaskId.get(taskId).closeHandleMenu();
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
}
}
@@ -926,19 +997,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
- private class TaskCornersListenerImpl
- implements DesktopModeWindowDecoration.TaskCornersListener {
+ private class ExclusionRegionListenerImpl
+ implements ExclusionRegionListener {
@Override
- public void onTaskCornersChanged(int taskId, Region corner) {
- mDesktopModeController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
- mDesktopTasksController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
+ public void onExclusionRegionChanged(int taskId, Region region) {
+ mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region));
}
@Override
- public void onTaskCornersRemoved(int taskId) {
- mDesktopModeController.ifPresent(d -> d.removeCornersForTask(taskId));
- mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId));
+ public void onExclusionRegionDismissed(int taskId) {
+ mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId));
}
}
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 a359395711e3..84ec6b389c4a 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
@@ -17,12 +17,15 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -36,13 +39,16 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
+import android.widget.ImageButton;
import android.window.WindowContainerTransaction;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -52,6 +58,7 @@ import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationVi
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Supplier;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -69,6 +76,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
+ private View.OnLongClickListener mOnCaptionLongClickListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -80,15 +88,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final Point mPositionInParent = new Point();
private HandleMenu mHandleMenu;
+ private MaximizeMenu mMaximizeMenu;
+
private ResizeVeil mResizeVeil;
private Drawable mAppIcon;
private CharSequence mAppName;
- private TaskCornersListener mCornersListener;
+ private ExclusionRegionListener mExclusionRegionListener;
private final Set<IBinder> mTransitionsPausingRelayout = new HashSet<>();
private int mRelayoutBlock;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
DesktopModeWindowDecoration(
Context context,
@@ -96,38 +107,55 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
+ Configuration windowDecorConfig,
Handler handler,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface);
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
+ }
+
+ DesktopModeWindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Configuration windowDecorConfig,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
+ SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ windowContainerTransactionSupplier, surfaceControlViewHostFactory);
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
loadAppInfo();
}
- @Override
- protected Configuration getConfigurationWithOverrides(
- ActivityManager.RunningTaskInfo taskInfo) {
- Configuration configuration = taskInfo.getConfiguration();
- if (DesktopTasksController.isDesktopDensityOverrideSet()) {
- // Density is overridden for desktop tasks. Keep system density for window decoration.
- configuration.densityDpi = mContext.getResources().getConfiguration().densityDpi;
- }
- return configuration;
- }
-
void setCaptionListeners(
View.OnClickListener onCaptionButtonClickListener,
- View.OnTouchListener onCaptionTouchListener) {
+ View.OnTouchListener onCaptionTouchListener,
+ View.OnLongClickListener onLongClickListener) {
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
mOnCaptionTouchListener = onCaptionTouchListener;
+ mOnCaptionLongClickListener = onLongClickListener;
}
- void setCornersListener(TaskCornersListener cornersListener) {
- mCornersListener = cornersListener;
+ void setExclusionRegionListener(ExclusionRegionListener exclusionRegionListener) {
+ mExclusionRegionListener = exclusionRegionListener;
}
void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
@@ -148,7 +176,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return;
}
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
// Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
// synced with the buffer transaction (that draws the View). Both will be shown on screen
// at the same, whereas applying them independently causes flickering. See b/270202228.
@@ -178,9 +206,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+ // The configuration used to lay out the window decoration. The system context's config is
+ // used when the task density has been overridden to a custom density so that the resources
+ // and views of the decoration aren't affected and match the rest of the System UI, if not
+ // then just use the task's configuration. A copy is made instead of using the original
+ // reference so that the configuration isn't mutated on config changes and diff checks can
+ // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+ // See b/301119301.
+ // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+ // instead of using a whole Configuration as a parameter.
+ final Configuration windowDecorConfig = new Configuration();
+ windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+ ? mContext.getResources().getConfiguration() // Use system context.
+ : mTaskInfo.configuration); // Use task configuration.
+ mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
mRelayoutParams.mCornerRadius =
(int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
@@ -207,6 +249,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
+ mOnCaptionLongClickListener,
mAppName,
mAppIcon
);
@@ -218,9 +261,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (!mTaskInfo.isFocused) {
closeHandleMenu();
+ closeMaximizeMenu();
}
if (!isDragResizeable) {
+ if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
+ // We still want to track caption bar's exclusion region on a non-resizeable task.
+ updateExclusionRegion();
+ }
closeDragResizeListener();
return;
}
@@ -248,13 +296,58 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int resize_corner = mResult.mRootView.getResources()
.getDimensionPixelSize(R.dimen.freeform_resize_corner);
- // If either task geometry or position have changed, update this task's cornersListener
+ // If either task geometry or position have changed, update this task's
+ // exclusion region listener
if (mDragResizeListener.setGeometry(
mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop)
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
- mCornersListener.onTaskCornersChanged(mTaskInfo.taskId, getGlobalCornersRegion());
+ updateExclusionRegion();
+ }
+
+ if (isMaximizeMenuActive()) {
+ if (!mTaskInfo.isVisible()) {
+ closeMaximizeMenu();
+ } else {
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ }
}
- mPositionInParent.set(mTaskInfo.positionInParent);
+ }
+
+ private PointF calculateMaximizeMenuPosition() {
+ final PointF position = new PointF();
+ final Resources resources = mContext.getResources();
+ final DisplayLayout displayLayout =
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ if (displayLayout == null) return position;
+
+ final int displayWidth = displayLayout.width();
+ final int displayHeight = displayLayout.height();
+ final int captionHeight = getCaptionHeight(mTaskInfo.getWindowingMode());
+
+ final ImageButton maximizeWindowButton =
+ mResult.mRootView.findViewById(R.id.maximize_window);
+ final int[] maximizeButtonLocation = new int[2];
+ maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
+
+ final int menuWidth = loadDimensionPixelSize(
+ resources, R.dimen.desktop_mode_maximize_menu_width);
+ final int menuHeight = loadDimensionPixelSize(
+ resources, R.dimen.desktop_mode_maximize_menu_height);
+
+ float menuLeft = (mPositionInParent.x + maximizeButtonLocation[0]);
+ float menuTop = (mPositionInParent.y + captionHeight);
+ final float menuRight = menuLeft + menuWidth;
+ final float menuBottom = menuTop + menuHeight;
+
+ // If the menu is out of screen bounds, shift it up/left as needed
+ if (menuRight > displayWidth) {
+ menuLeft = (displayWidth - menuWidth);
+ }
+ if (menuBottom > displayHeight) {
+ menuTop = (displayHeight - menuHeight);
+ }
+
+ return new PointF(menuLeft, menuTop);
}
boolean isHandleMenuActive() {
@@ -335,6 +428,29 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
+ * Create and display maximize menu window
+ */
+ void createMaximizeMenu() {
+ mMaximizeMenu = new MaximizeMenu(mSyncQueue, mRootTaskDisplayAreaOrganizer,
+ mDisplayController, mTaskInfo, mOnCaptionButtonClickListener, mContext,
+ calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
+ mMaximizeMenu.show();
+ }
+
+ /**
+ * Close the maximize menu window
+ */
+ void closeMaximizeMenu() {
+ if (!isMaximizeMenuActive()) return;
+ mMaximizeMenu.close();
+ mMaximizeMenu = null;
+ }
+
+ boolean isMaximizeMenuActive() {
+ return mMaximizeMenu != null;
+ }
+
+ /**
* Create and display handle menu window
*/
void createHandleMenu() {
@@ -345,7 +461,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
.setOnTouchListener(mOnCaptionTouchListener)
.setLayoutId(mRelayoutParams.mLayoutResId)
.setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
- .setWindowingButtonsVisible(DesktopModeStatus.isProto2Enabled())
+ .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
.build();
mHandleMenu.show();
}
@@ -362,6 +478,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@Override
void releaseViews() {
closeHandleMenu();
+ closeMaximizeMenu();
super.releaseViews();
}
@@ -460,27 +577,43 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
public void close() {
closeDragResizeListener();
closeHandleMenu();
- mCornersListener.onTaskCornersRemoved(mTaskInfo.taskId);
+ mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
super.close();
}
- private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
- if (DesktopModeStatus.isProto1Enabled()) {
- return R.layout.desktop_mode_app_controls_window_decor;
- }
+ private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
}
+ private void updatePositionInParent() {
+ mPositionInParent.set(mTaskInfo.positionInParent);
+ }
+
+ private void updateExclusionRegion() {
+ // An outdated position in parent is one reason for this to be called; update it here.
+ updatePositionInParent();
+ mExclusionRegionListener
+ .onExclusionRegionChanged(mTaskInfo.taskId, getGlobalExclusionRegion());
+ }
+
/**
- * Create a new region out of the corner rects of this task.
+ * Create a new exclusion region from the corner rects (if resizeable) and caption bounds
+ * of this task.
*/
- Region getGlobalCornersRegion() {
- Region cornersRegion = mDragResizeListener.getCornersRegion();
- cornersRegion.translate(mPositionInParent.x, mPositionInParent.y);
- return cornersRegion;
+ private Region getGlobalExclusionRegion() {
+ Region exclusionRegion;
+ if (mTaskInfo.isResizeable) {
+ exclusionRegion = mDragResizeListener.getCornersRegion();
+ } else {
+ exclusionRegion = new Region();
+ }
+ exclusionRegion.union(new Rect(0, 0, mResult.mWidth,
+ getCaptionHeight(mTaskInfo.getWindowingMode())));
+ exclusionRegion.translate(mPositionInParent.x, mPositionInParent.y);
+ return exclusionRegion;
}
/**
@@ -494,8 +627,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
@Override
- int getCaptionHeightId() {
- return R.dimen.freeform_decor_caption_height;
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ ? R.dimen.desktop_mode_fullscreen_decor_caption_height
+ : R.dimen.desktop_mode_freeform_decor_caption_height;
+ }
+
+ private int getCaptionHeight(@WindowingMode int windowingMode) {
+ return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
}
/**
@@ -532,25 +671,34 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
SurfaceControl taskSurface,
Handler handler,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ final Configuration windowDecorConfig =
+ DesktopTasksController.isDesktopDensityOverrideSet()
+ ? context.getResources().getConfiguration() // Use system context
+ : taskInfo.configuration; // Use task configuration
return new DesktopModeWindowDecoration(
context,
displayController,
taskOrganizer,
taskInfo,
taskSurface,
+ windowDecorConfig,
handler,
choreographer,
- syncQueue);
+ syncQueue,
+ rootTaskDisplayAreaOrganizer);
}
}
- interface TaskCornersListener {
- /** Inform the implementing class of this task's change in corner resize handles */
- void onTaskCornersChanged(int taskId, Region corner);
+ interface ExclusionRegionListener {
+ /** Inform the implementing class of this task's change in region resize handles */
+ void onExclusionRegionChanged(int taskId, Region region);
- /** Inform the implementing class that this task no longer needs its corners tracked,
- * likely due to it closing. */
- void onTaskCornersRemoved(int taskId);
+ /**
+ * Inform the implementing class that this task no longer needs an exclusion region,
+ * likely due to it closing.
+ */
+ void onExclusionRegionDismissed(int taskId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index e32bd42acf74..cb0a6c733fe3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -143,6 +143,24 @@ public class DragPositioningCallbackUtility {
}
/**
+ * Calculates the new position of the top edge of the task and returns true if it is below the
+ * disallowed area.
+ *
+ * @param disallowedAreaForEndBoundsHeight the height of the area that where the task positioner
+ * should not finalize the bounds using WCT#setBounds
+ * @param taskBoundsAtDragStart the bounds of the task on the first drag input event
+ * @param repositionStartPoint initial input coordinate
+ * @param y the y position of the motion event
+ * @return true if the top of the task is below the disallowed area
+ */
+ static boolean isBelowDisallowedArea(int disallowedAreaForEndBoundsHeight,
+ Rect taskBoundsAtDragStart, PointF repositionStartPoint, float y) {
+ final float deltaY = y - repositionStartPoint.y;
+ final float topPosition = taskBoundsAtDragStart.top + deltaY;
+ return topPosition > disallowedAreaForEndBoundsHeight;
+ }
+
+ /**
* Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If
* the bounds are outside of the stable bounds, they are shifted to place task at the top of the
* stable bounds.
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 e0ee25242550..389db62ef9d2 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
@@ -125,7 +125,9 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
}
mTaskOrganizer.applyTransaction(wct);
} else if (mCtrlType == CTRL_TYPE_UNDEFINED
- && y > mDisallowedAreaForEndBoundsHeight) {
+ && DragPositioningCallbackUtility.isBelowDisallowedArea(
+ mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
+ y)) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index ac4a597c15d1..f82b212c344c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -26,7 +26,10 @@ import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
@@ -167,10 +170,9 @@ class HandleMenu {
desktopBtn.setOnClickListener(mOnClickListener);
// The button corresponding to the windowing mode that the task is currently in uses a
// different color than the others.
- final ColorStateList activeColorStateList = ColorStateList.valueOf(
- mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
- final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
- mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
+ final int[] iconColors = getWindowingIconColor();
+ final ColorStateList inActiveColorStateList = ColorStateList.valueOf(iconColors[0]);
+ final ColorStateList activeColorStateList = ColorStateList.valueOf(iconColors[1]);
fullscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
? activeColorStateList : inActiveColorStateList);
@@ -186,12 +188,34 @@ class HandleMenu {
// More Actions pill setup.
final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView();
final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
- closeBtn.setOnClickListener(mOnClickListener);
+ if (shouldShowCloseButton()) {
+ closeBtn.setVisibility(View.GONE);
+ } else {
+ closeBtn.setVisibility(View.VISIBLE);
+ closeBtn.setOnClickListener(mOnClickListener);
+ }
final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button);
selectBtn.setOnClickListener(mOnClickListener);
}
/**
+ * Returns array of windowing icon color based on current UI theme. First element of the
+ * array is for inactive icons and the second is for active icons.
+ */
+ private int[] getWindowingIconColor() {
+ final int mode = mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
+ final TypedArray typedArray = mContext.obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.materialColorOnSurface,
+ com.android.internal.R.attr.materialColorPrimary});
+ final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK);
+ final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
+ typedArray.recycle();
+ return new int[] {inActiveColor, activeColor};
+ }
+
+ /**
* Updates the handle menu pills' position variables to reflect their next positions
*/
private void updateHandleMenuPillPositions() {
@@ -228,7 +252,6 @@ class HandleMenu {
/**
* Update pill layout, in case task changes have caused positioning to change.
- * @param t
*/
void relayout(SurfaceControl.Transaction t) {
if (mAppInfoPill != null) {
@@ -236,7 +259,7 @@ class HandleMenu {
t.setPosition(mAppInfoPill.mWindowSurface,
mAppInfoPillPosition.x, mAppInfoPillPosition.y);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
- final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
+ final boolean shouldShowWindowingPill = DesktopModeStatus.isEnabled();
if (shouldShowWindowingPill) {
t.setPosition(mWindowingPill.mWindowSurface,
mWindowingPillPosition.x, mWindowingPillPosition.y);
@@ -245,10 +268,12 @@ class HandleMenu {
mMoreActionsPillPosition.x, mMoreActionsPillPosition.y);
}
}
+
/**
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
+ *
* @param ev the MotionEvent to compare against.
*/
void checkClickEvent(MotionEvent ev) {
@@ -267,6 +292,7 @@ class HandleMenu {
* A valid menu input is one of the following:
* An input that happens in the menu views.
* Any input before the views have been laid out.
+ *
* @param inputPoint the input to compare against.
*/
boolean isValidMenuInput(PointF inputPoint) {
@@ -297,7 +323,6 @@ class HandleMenu {
/**
* Check if the views for handle menu can be seen.
- * @return
*/
private boolean viewsLaidOut() {
return mAppInfoPill.mWindowViewHost.getView().isLaidOut();
@@ -318,8 +343,11 @@ class HandleMenu {
R.dimen.desktop_mode_handle_menu_app_info_pill_height);
mWindowingPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_windowing_pill_height);
- mMoreActionsPillHeight = loadDimensionPixelSize(resources,
- R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
+ mMoreActionsPillHeight = shouldShowCloseButton()
+ ? loadDimensionPixelSize(resources,
+ R.dimen.desktop_mode_handle_menu_more_actions_pill_freeform_height)
+ : loadDimensionPixelSize(resources,
+ R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
mShadowRadius = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_shadow_radius);
mCornerRadius = loadDimensionPixelSize(resources,
@@ -333,6 +361,10 @@ class HandleMenu {
return resources.getDimensionPixelSize(resourceId);
}
+ private boolean shouldShowCloseButton() {
+ return mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ }
+
void close() {
mAppInfoPill.releaseView();
mAppInfoPill = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
new file mode 100644
index 000000000000..050d1e9df392
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.PixelFormat
+import android.graphics.PointF
+import android.view.LayoutInflater
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.SurfaceControlViewHost
+import android.view.View.OnClickListener
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import android.widget.Button
+import android.window.TaskConstants
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.windowdecor.WindowDecoration.AdditionalWindow
+import java.util.function.Supplier
+
+
+/**
+ * Menu that appears when user long clicks the maximize button. Gives the user the option to
+ * maximize the task or snap the task to the right or left half of the screen.
+ */
+class MaximizeMenu(
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val displayController: DisplayController,
+ private val taskInfo: RunningTaskInfo,
+ private val onClickListener: OnClickListener,
+ private val decorWindowContext: Context,
+ private val menuPosition: PointF,
+ private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
+) {
+ private var maximizeMenu: AdditionalWindow? = null
+ private lateinit var viewHost: SurfaceControlViewHost
+ private lateinit var leash: SurfaceControl
+ private val shadowRadius = loadDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_shadow_radius
+ ).toFloat()
+ private val cornerRadius = loadDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_corner_radius
+ ).toFloat()
+
+ /** Position the menu relative to the caption's position. */
+ fun positionMenu(position: PointF, t: Transaction) {
+ menuPosition.set(position)
+ t.setPosition(leash, menuPosition.x, menuPosition.y)
+ }
+
+ /** Creates and shows the maximize window. */
+ fun show() {
+ if (maximizeMenu != null) return
+ createMaximizeMenu()
+ setupMaximizeMenu()
+ }
+
+ /** Closes the maximize window and releases its view. */
+ fun close() {
+ maximizeMenu?.releaseView()
+ maximizeMenu = null
+ }
+
+ /** Create a maximize menu that is attached to the display area. */
+ private fun createMaximizeMenu() {
+ val t = transactionSupplier.get()
+ val v = LayoutInflater.from(decorWindowContext).inflate(
+ R.layout.desktop_mode_window_decor_maximize_menu,
+ null // Root
+ )
+ val builder = SurfaceControl.Builder()
+ rootTdaOrganizer.attachToDisplayArea(taskInfo.displayId, builder)
+ leash = builder
+ .setName("Maximize Menu")
+ .setContainerLayer()
+ .build()
+ val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
+ val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
+ val lp = WindowManager.LayoutParams(
+ menuWidth,
+ menuHeight,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT
+ )
+ lp.title = "Maximize Menu for Task=" + taskInfo.taskId
+ lp.setTrustedOverlay()
+ val windowManager = WindowlessWindowManager(
+ taskInfo.configuration,
+ leash,
+ null // HostInputToken
+ )
+ viewHost = SurfaceControlViewHost(decorWindowContext,
+ displayController.getDisplay(taskInfo.displayId), windowManager,
+ "MaximizeMenu")
+ viewHost.setView(v, lp)
+
+ // Bring menu to front when open
+ t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU)
+ .setPosition(leash, menuPosition.x, menuPosition.y)
+ .setWindowCrop(leash, menuWidth, menuHeight)
+ .setShadowRadius(leash, shadowRadius)
+ .setCornerRadius(leash, cornerRadius)
+ .show(leash)
+ maximizeMenu = AdditionalWindow(leash, viewHost, transactionSupplier)
+
+ syncQueue.runInSync { transaction ->
+ transaction.merge(t)
+ t.close()
+ }
+ }
+
+ private fun loadDimensionPixelSize(resourceId: Int): Int {
+ return if (resourceId == Resources.ID_NULL) {
+ 0
+ } else {
+ decorWindowContext.resources.getDimensionPixelSize(resourceId)
+ }
+ }
+
+ private fun setupMaximizeMenu() {
+ val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
+
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_maximize_button
+ ).setOnClickListener(onClickListener)
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_snap_right_button
+ ).setOnClickListener(onClickListener)
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_snap_left_button
+ ).setOnClickListener(onClickListener)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index bfce72bcadf0..09fc3dacf6f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -49,11 +49,13 @@ public class ResizeVeil {
private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
private final Drawable mAppIcon;
+ private ImageView mIconView;
private SurfaceControl mParentSurface;
private SurfaceControl mVeilSurface;
private final RunningTaskInfo mTaskInfo;
private SurfaceControlViewHost mViewHost;
private final Display mDisplay;
+ private ValueAnimator mVeilAnimator;
public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display,
@@ -97,8 +99,8 @@ public class ResizeVeil {
mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil");
mViewHost.setView(v, lp);
- final ImageView appIcon = mViewHost.getView().findViewById(R.id.veil_application_icon);
- appIcon.setImageDrawable(mAppIcon);
+ mIconView = mViewHost.getView().findViewById(R.id.veil_application_icon);
+ mIconView.setImageDrawable(mAppIcon);
}
/**
@@ -123,17 +125,27 @@ public class ResizeVeil {
relayout(taskBounds, t);
if (fadeIn) {
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(0f, 1f);
- animator.setDuration(RESIZE_ALPHA_DURATION);
- animator.addUpdateListener(animation -> {
- t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
+ mVeilAnimator = new ValueAnimator();
+ mVeilAnimator.setFloatValues(0f, 1f);
+ mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
+ mVeilAnimator.addUpdateListener(animation -> {
+ t.setAlpha(mVeilSurface, mVeilAnimator.getAnimatedFraction());
t.apply();
});
+ final ValueAnimator iconAnimator = new ValueAnimator();
+ iconAnimator.setFloatValues(0f, 1f);
+ iconAnimator.setDuration(RESIZE_ALPHA_DURATION);
+ iconAnimator.addUpdateListener(animation -> {
+ mIconView.setAlpha(animation.getAnimatedFraction());
+ });
+
t.show(mVeilSurface)
.addTransactionCommittedListener(
- mContext.getMainExecutor(), () -> animator.start())
+ mContext.getMainExecutor(), () -> {
+ mVeilAnimator.start();
+ iconAnimator.start();
+ })
.setAlpha(mVeilSurface, 0);
} else {
// Show the veil immediately at full opacity.
@@ -172,11 +184,17 @@ public class ResizeVeil {
/**
* Calls relayout to update task and veil bounds.
+ * Finishes veil fade in if animation is currently running; this is to prevent empty space
+ * being visible behind the transparent veil during a fast resize.
*
* @param t a transaction to be applied in sync with the veil draw.
* @param newBounds bounds to update veil to.
*/
public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) {
+ if (mVeilAnimator != null && mVeilAnimator.isStarted()) {
+ // TODO(b/300145351): Investigate why ValueAnimator#end does not work here.
+ mVeilAnimator.setCurrentPlayTime(RESIZE_ALPHA_DURATION);
+ }
relayout(newBounds, t);
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}
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 c9c58de6e82a..303954a2d9fb 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
@@ -142,7 +142,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
// won't be called.
mDesktopWindowDecoration.hideResizeVeil();
}
- } else if (y > mDisallowedAreaForEndBoundsHeight) {
+ } else if (DragPositioningCallbackUtility.isBelowDisallowedArea(
+ mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
+ y)) {
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(),
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 0b0d9d5086f4..07fee4316c08 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
@@ -17,8 +17,10 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -115,6 +117,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
SurfaceControl mCaptionContainerSurface;
private WindowlessWindowManager mCaptionWindowManager;
private SurfaceControlViewHost mViewHost;
+ private Configuration mWindowDecorConfig;
private final Binder mOwner = new Binder();
private final Rect mCaptionInsetsRect = new Rect();
@@ -125,8 +128,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
- SurfaceControl taskSurface) {
- this(context, displayController, taskOrganizer, taskInfo, taskSurface,
+ SurfaceControl taskSurface,
+ Configuration windowDecorConfig) {
+ this(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
}
@@ -137,6 +141,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
+ Configuration windowDecorConfig,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
@@ -152,17 +157,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
- mDecorWindowContext = mContext.createConfigurationContext(
- getConfigurationWithOverrides(mTaskInfo));
- }
-
- /**
- * Get {@link Configuration} from supplied {@link RunningTaskInfo}.
- *
- * Allows values to be overridden before returning the configuration.
- */
- protected Configuration getConfigurationWithOverrides(RunningTaskInfo taskInfo) {
- return taskInfo.getConfiguration();
+ mWindowDecorConfig = windowDecorConfig;
+ mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
}
/**
@@ -179,7 +175,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
RelayoutResult<T> outResult) {
outResult.reset();
- final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
}
@@ -198,18 +193,26 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
outResult.mRootView = rootView;
rootView = null; // Clear it just in case we use it accidentally
- final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
- if (oldTaskConfig.densityDpi != taskConfig.densityDpi
+
+ final int oldDensityDpi = mWindowDecorConfig.densityDpi;
+ final int oldNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
+ : mTaskInfo.getConfiguration();
+ final int newDensityDpi = mWindowDecorConfig.densityDpi;
+ final int newNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (oldDensityDpi != newDensityDpi
|| mDisplay == null
|| mDisplay.getDisplayId() != mTaskInfo.displayId
- || oldLayoutResId != mLayoutResId) {
+ || oldLayoutResId != mLayoutResId
+ || oldNightMode != newNightMode) {
releaseViews();
if (!obtainDisplayOrRegisterListener()) {
outResult.mRootView = null;
return;
}
- mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
+ mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
+ mDecorWindowContext.setTheme(mContext.getThemeResId());
if (params.mLayoutResId != 0) {
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
.inflate(params.mLayoutResId, null);
@@ -222,7 +225,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
final Resources resources = mDecorWindowContext.getResources();
+ final Configuration taskConfig = mTaskInfo.getConfiguration();
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
+ final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN;
outResult.mWidth = taskBounds.width();
outResult.mHeight = taskBounds.height();
@@ -282,13 +288,24 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
final Point taskPosition = mTaskInfo.positionInParent;
- startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
- .setShadowRadius(mTaskSurface, shadowRadius)
+ if (isFullscreen) {
+ // Setting the task crop to the width/height stops input events from being sent to
+ // some regions of the app window. See b/300324920
+ // TODO(b/296921174): investigate whether crop/position needs to be set by window
+ // decorations at all when transition handlers are already taking ownership of the task
+ // surface placement/crop, especially when in fullscreen where tasks cannot be
+ // drag-resized by the window decoration.
+ startT.setWindowCrop(mTaskSurface, null);
+ finishT.setWindowCrop(mTaskSurface, null);
+ } else {
+ startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ }
+ startT.setShadowRadius(mTaskSurface, shadowRadius)
.setColor(mTaskSurface, mTmpColor)
.show(mTaskSurface);
finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
- .setShadowRadius(mTaskSurface, shadowRadius)
- .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ .setShadowRadius(mTaskSurface, shadowRadius);
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
@@ -325,7 +342,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
}
- int getCaptionHeightId() {
+ int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
@@ -447,7 +464,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
* Adds caption inset source to a WCT
*/
public void addCaptionInset(WindowContainerTransaction wct) {
- final int captionHeightId = getCaptionHeightId();
+ final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
return;
}
@@ -470,6 +487,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mCaptionX;
int mCaptionY;
+ Configuration mWindowDecorConfig;
+
boolean mApplyStartTransactionOnDraw;
void reset() {
@@ -484,6 +503,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionY = 0;
mApplyStartTransactionOnDraw = false;
+ mWindowDecorConfig = null;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index a9eb8829bd2c..6b59ccec5148 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -5,6 +5,7 @@ import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.view.View
+import android.view.View.OnLongClickListener
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
@@ -19,6 +20,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: View.OnClickListener,
+ onLongClickListener: OnLongClickListener,
appName: CharSequence,
appIcon: Drawable
) : DesktopModeWindowDecorationViewHolder(rootView) {
@@ -39,6 +41,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
openMenuButton.setOnTouchListener(onCaptionTouchListener)
closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+ maximizeWindowButton.onLongClickListener = onLongClickListener
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
appIconImageView.setImageDrawable(appIcon)
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index b062fbd510ca..0058d115ce56 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -44,18 +44,57 @@ filegroup {
}
filegroup {
- name: "WMShellFlickerTestsSplitScreen-src",
+ name: "WMShellFlickerTestsPipCommon-src",
+ srcs: ["src/com/android/wm/shell/flicker/pip/common/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPipApps-src",
+ srcs: ["src/com/android/wm/shell/flicker/pip/apps/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenBase-src",
srcs: [
- "src/com/android/wm/shell/flicker/splitscreen/*.kt",
"src/com/android/wm/shell/flicker/splitscreen/benchmark/*.kt",
],
}
filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroup1-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/splitscreen/A*.kt",
+ "src/com/android/wm/shell/flicker/splitscreen/B*.kt",
+ "src/com/android/wm/shell/flicker/splitscreen/C*.kt",
+ "src/com/android/wm/shell/flicker/splitscreen/D*.kt",
+ "src/com/android/wm/shell/flicker/splitscreen/E*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroup2-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/splitscreen/*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerServicePlatinumTests-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
+ "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt",
+ "src/com/android/wm/shell/flicker/service/common/**/*.kt",
+ ],
+}
+
+filegroup {
name: "WMShellFlickerServiceTests-src",
srcs: [
"src/com/android/wm/shell/flicker/service/**/*.kt",
],
+ exclude_srcs: [
+ "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
+ ],
}
java_library {
@@ -80,6 +119,20 @@ java_library {
],
}
+java_library {
+ name: "wm-shell-flicker-platinum-tests",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: [
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+ static_libs: [
+ "wm-shell-flicker-utils",
+ ],
+}
+
java_defaults {
name: "WMShellFlickerTestsDefault",
manifest: "manifests/AndroidManifest.xml",
@@ -97,6 +150,7 @@ java_defaults {
"flickertestapplib",
"flickerlib",
"flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
"platform-test-annotations",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
@@ -122,8 +176,13 @@ android_test {
exclude_srcs: [
":WMShellFlickerTestsBubbles-src",
":WMShellFlickerTestsPip-src",
- ":WMShellFlickerTestsSplitScreen-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ":WMShellFlickerTestsPipApps-src",
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ":WMShellFlickerTestsSplitScreenGroup2-src",
+ ":WMShellFlickerTestsSplitScreenBase-src",
":WMShellFlickerServiceTests-src",
+ ":WMShellFlickerServicePlatinumTests-src",
],
}
@@ -148,18 +207,49 @@ android_test {
srcs: [
":WMShellFlickerTestsBase-src",
":WMShellFlickerTestsPip-src",
+ ":WMShellFlickerTestsPipCommon-src",
],
}
android_test {
- name: "WMShellFlickerTestsSplitScreen",
+ name: "WMShellFlickerTestsPipApps",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestPip.xml"],
+ package_name: "com.android.wm.shell.flicker.pip.apps",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsPipApps-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroup1",
defaults: ["WMShellFlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
package_name: "com.android.wm.shell.flicker.splitscreen",
instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
srcs: [
":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsSplitScreen-src",
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroup2",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ":WMShellFlickerTestsSplitScreenGroup2-src",
+ ],
+ exclude_srcs: [
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
],
}
@@ -174,3 +264,15 @@ android_test {
":WMShellFlickerServiceTests-src",
],
}
+
+android_test {
+ name: "WMShellFlickerServicePlatinumTests",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestService.xml"],
+ package_name: "com.android.wm.shell.flicker.service",
+ instrumentation_target_package: "com.android.wm.shell.flicker.service",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
index c8a96377800f..b13e9a248575 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
@@ -57,6 +57,14 @@
<option name="test-file-name" value="{MODULE}.apk"/>
<option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
@@ -73,7 +81,9 @@
<option name="shell-timeout" value="6600s"/>
<option name="test-timeout" value="6000s"/>
<option name="hidden-api-checks" value="false"/>
+ <!-- TODO(b/288396763): re-enable when PerfettoListener is fixed
<option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ -->
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
<option name="instrumentation-arg"
@@ -90,11 +100,11 @@
<option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.pip/files"/>
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/>
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
index 6a87de47def4..ae130b8f6f7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
@@ -45,9 +45,13 @@
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
<!-- Enable bubble notification-->
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
- <!-- Allow the test to write directly to /sdcard/ -->
- <application android:requestLegacyExternalStorage="true" android:largeHeap="true">
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
<uses-library android:name="android.test.runner"/>
<service android:name=".NotificationListener"
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
index 5a8155a66d30..fa42a4570b7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
@@ -17,6 +17,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.flicker.pip">
+ <!-- Enable mocking GPS location -->
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.wm.shell.flicker.pip"
android:label="WindowManager Flicker Tests">
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 36acb58d6139..adf92d8854ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -28,7 +28,6 @@ import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.utils.appWindowKeepVisible
import com.android.wm.shell.flicker.utils.layerKeepVisible
-
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
@@ -37,9 +36,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val letterboxApp = LetterboxAppHelper(instrumentation)
- @JvmField
- @Rule
- val letterboxRule: LetterboxRule = LetterboxRule()
+ @JvmField @Rule val letterboxRule: LetterboxRule = LetterboxRule()
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -53,7 +50,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
}
@Before
- fun before() {
+ fun setUp() {
Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
index 5a1136f97c6f..744e8c2eb06f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -23,16 +23,14 @@ import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
-/**
- * JUnit Rule to handle letterboxStyles and states
- */
+/** JUnit Rule to handle letterboxStyles and states */
class LetterboxRule(
- private val withLetterboxEducationEnabled: Boolean = false,
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
- private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+ private val withLetterboxEducationEnabled: Boolean = false,
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
) : TestRule {
- private val execAdb: (String) -> String = {cmd -> cmdHelper.executeShellCommand(cmd)}
+ private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) }
private lateinit var _letterboxStyle: MutableMap<String, String>
val letterboxStyle: Map<String, String>
@@ -62,8 +60,7 @@ class LetterboxRule(
var hasLetterboxEducationStateChanged = false
if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
hasLetterboxEducationStateChanged = true
- execAdb("wm set-letterbox-style --isEducationEnabled " +
- withLetterboxEducationEnabled)
+ execAdb("wm set-letterbox-style --isEducationEnabled " + withLetterboxEducationEnabled)
}
return try {
object : Statement() {
@@ -74,8 +71,8 @@ class LetterboxRule(
}
} finally {
if (hasLetterboxEducationStateChanged) {
- execAdb("wm set-letterbox-style --isEducationEnabled " +
- isLetterboxEducationEnabled
+ execAdb(
+ "wm set-letterbox-style --isEducationEnabled " + isLetterboxEducationEnabled
)
}
resetLetterboxStyle()
@@ -100,9 +97,10 @@ class LetterboxRule(
execAdb("wm reset-letterbox-style")
}
- private fun asInt(str: String?): Int? = try {
- str?.toInt()
- } catch (e: NumberFormatException) {
- null
- }
-} \ No newline at end of file
+ private fun asInt(str: String?): Int? =
+ try {
+ str?.toInt()
+ } catch (e: NumberFormatException) {
+ null
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index 67d5718e6c1f..1e5e42fb077e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -66,7 +66,8 @@ class OpenAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(fl
*/
@Postsubmit
@Test
- fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) }
+ fun letterboxAppFocusedAtEnd() =
+ flicker.assertEventLog { focusChanges(letterboxApp.packageName) }
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
index e6ca261a317f..2fa1ec386781 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -54,9 +54,7 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setup {
- letterboxTranslucentLauncherApp.launchViaIntent(wmHelper)
- }
+ setup { letterboxTranslucentLauncherApp.launchViaIntent(wmHelper) }
transitions {
waitAndGetLaunchTransparent()?.click() ?: error("Launch Transparent not found")
}
@@ -66,9 +64,7 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA
}
}
- /**
- * Checks the transparent activity is launched on top of the opaque one
- */
+ /** Checks the transparent activity is launched on top of the opaque one */
@Postsubmit
@Test
fun translucentActivityIsLaunchedOnTopOfOpaqueActivity() {
@@ -79,18 +75,14 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA
}
}
- /**
- * Checks that the activity is letterboxed
- */
+ /** Checks that the activity is letterboxed */
@Postsubmit
@Test
fun translucentActivityIsLetterboxed() {
flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
}
- /**
- * Checks that the translucent activity inherits bounds from the opaque one.
- */
+ /** Checks that the translucent activity inherits bounds from the opaque one. */
@Postsubmit
@Test
fun translucentActivityInheritsBoundsFromOpaqueActivity() {
@@ -100,29 +92,26 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA
}
}
- /**
- * Checks that the translucent activity has rounded corners
- */
+ /** Checks that the translucent activity has rounded corners */
@Postsubmit
@Test
fun translucentActivityHasRoundedCorners() {
- flicker.assertLayersEnd {
- this.hasRoundedCorners(letterboxTranslucentApp)
- }
+ flicker.assertLayersEnd { this.hasRoundedCorners(letterboxTranslucentApp) }
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
- * navigation modes.
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
+ * modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return LegacyFlickerTestFactory
- .nonRotationTests(supportedRotations = listOf(Rotation.ROTATION_90))
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
index d3f3c5b7c672..b74aa1d7bf73 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -49,8 +49,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) :
- BaseAppCompat(flicker) {
+class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
new file mode 100644
index 000000000000..ba2b3e7e2781
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.os.Build
+import android.tools.common.datatypes.Rect
+import android.platform.test.annotations.Postsubmit
+import android.system.helpers.CommandsHelper
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test rotating an immersive app in fullscreen.
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:RotateImmersiveAppInFullscreenTest`
+ *
+ * Actions:
+ * ```
+ * Rotate the device by 90 degrees to trigger a rotation through sensors
+ * Verify that the button exists
+ * ```
+ *
+ * Notes:
+ * ```
+ * Some default assertions that are inherited from
+ * the `BaseTest` are ignored due to the nature of the immersive apps.
+ *
+ * This test only works with Cuttlefish devices.
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
+
+ private val immersiveApp = LetterboxAppHelper(instrumentation,
+ launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL,
+ component =
+ ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent())
+
+ private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+ private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) }
+
+ protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+
+ private val isCuttlefishDevice: Boolean = Build.MODEL.contains("Cuttlefish")
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ setStartRotation()
+ immersiveApp.launchViaIntent(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Display not found")
+ }
+ transitions {
+ if (isCuttlefishDevice) {
+ // Simulates a device rotation through sensors because the rotation button
+ // only appears in a rotation event through sensors
+ execAdb("/vendor/bin/cuttlefish_sensor_injection rotate 0")
+ // verify rotation button existence
+ val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion")
+ uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT)
+ uiDevice.findObject(rotationButtonSelector)
+ ?: error("rotation button not found")
+ }
+ }
+ teardown {
+ immersiveApp.exit(wmHelper)
+ }
+ }
+
+ @Before
+ fun setUpForImmersiveAppTests() {
+ Assume.assumeTrue(isCuttlefishDevice)
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun taskBarWindowIsAlwaysVisible() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun navBarWindowIsAlwaysVisible() {
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarWindowIsAlwaysVisible() {
+ }
+
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ }
+
+ @Test
+ @Ignore("Not applicable to this CUJ. App is in immersive mode.")
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ }
+
+ /** Test that app is fullscreen by checking status bar and task bar visibility. */
+ @Postsubmit
+ @Test
+ fun appWindowFullScreen() {
+ flicker.assertWmEnd {
+ this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR)
+ .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
+ .visibleRegion(immersiveApp).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /** Test that app is in the original rotation we have set up. */
+ @Postsubmit
+ @Test
+ fun appInOriginalRotation() {
+ flicker.assertWmEnd {
+ this.hasRotation(Rotation.ROTATION_90)
+ }
+ }
+
+ companion object {
+ private var startDisplayBounds = Rect.EMPTY
+ const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher"
+
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90),
+ // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
index ea0392cee95a..9792c859cced 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
@@ -33,20 +33,20 @@ import org.junit.Rule
abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
- protected val letterboxTranslucentLauncherApp = LetterboxAppHelper(
- instrumentation,
- launcherName = ActivityOptions.LaunchTransparentActivity.LABEL,
- component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent()
- )
- protected val letterboxTranslucentApp = LetterboxAppHelper(
- instrumentation,
- launcherName = ActivityOptions.TransparentActivity.LABEL,
- component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent()
- )
+ protected val letterboxTranslucentLauncherApp =
+ LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.LaunchTransparentActivity.LABEL,
+ component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent()
+ )
+ protected val letterboxTranslucentApp =
+ LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.TransparentActivity.LABEL,
+ component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent()
+ )
- @JvmField
- @Rule
- val letterboxRule: LetterboxRule = LetterboxRule()
+ @JvmField @Rule val letterboxRule: LetterboxRule = LetterboxRule()
@Before
fun before() {
@@ -54,10 +54,7 @@ abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(f
}
protected fun FlickerTestData.waitAndGetLaunchTransparent(): UiObject2? =
- device.wait(
- Until.findObject(By.text("Launch Transparent")),
- FIND_TIMEOUT
- )
+ device.wait(Until.findObject(By.text("Launch Transparent")), FIND_TIMEOUT)
protected fun FlickerTestData.goBack() = device.pressBack()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 5c7d1d8df2e8..bc095bbacc5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -31,6 +31,7 @@ import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
+import com.android.server.wm.flicker.helpers.MultiWindowUtils
import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
@@ -47,7 +48,7 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker)
private val uid =
context.packageManager
- .getApplicationInfo(testApp.`package`, PackageManager.ApplicationInfoFlags.of(0))
+ .getApplicationInfo(testApp.packageName, PackageManager.ApplicationInfoFlags.of(0))
.uid
@JvmOverloads
@@ -56,8 +57,11 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker)
): FlickerBuilder.() -> Unit {
return {
setup {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 1")
notifyManager.setBubblesAllowed(
- testApp.`package`,
+ testApp.packageName,
uid,
NotificationManager.BUBBLE_PREFERENCE_ALL
)
@@ -67,8 +71,11 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker)
}
teardown {
+ MultiWindowUtils.executeShellCommand(
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 0")
notifyManager.setBubblesAllowed(
- testApp.`package`,
+ testApp.packageName,
uid,
NotificationManager.BUBBLE_PREFERENCE_NONE
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index bc565bc5fd42..55039f59190b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -17,12 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.os.SystemClock
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
@@ -40,12 +39,10 @@ import org.junit.runners.Parameterized
* Switch in different bubble notifications
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FlakyTest(bugId = 217777115)
-open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) :
- BaseBubbleScreen(flicker) {
+class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
deleted file mode 100644
index abc6b9f9a746..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.bubble
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
- ChangeActiveActivityFromBubbleTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 3f28ae848d1f..9ca7bf113589 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -20,12 +20,12 @@ import android.content.Context
import android.graphics.Point
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.util.DisplayMetrics
import android.view.WindowManager
-import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Test
@@ -42,10 +42,9 @@ import org.junit.runners.Parameterized
* Dismiss a bubble notification
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
+class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val displaySize = DisplayMetrics()
@@ -80,7 +79,8 @@ open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbl
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
flicker.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp)
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(testApp, ComponentNameMatcher(className = "Bubbles!#"))
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 26aca1830889..b007e6b3535c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -24,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.view.WindowInsets
import android.view.WindowManager
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index 508539411aa0..4959672d865b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Test
@@ -39,10 +38,9 @@ import org.junit.runners.Parameterized
* The activity for the bubble is launched
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
+class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index a926bb7d85c3..0d95574aca06 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Test
@@ -38,10 +37,9 @@ import org.junit.runners.Parameterized
* Send a bubble notification
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
+class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index c335d3dc7f4b..213d596a6131 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -23,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.common.flicker.subject.region.RegionTraceSubject
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -80,7 +81,9 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
+ SplitScreenUtils.enterSplit(
+ wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
+ flicker.scenario.startRotation)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -126,9 +129,20 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
if (tapl.isTablet) {
flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
} else {
- // on phones home does not rotate in landscape, PiP enters back to portrait
- // orientation so use display bounds from that orientation for assertion
- flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) }
+ // on phones home screen does not rotate in landscape, PiP enters back to portrait
+ // orientation - if we go from landscape to portrait it should switch between the bounds
+ // otherwise it should be the same as tablet (i.e. portrait to portrait)
+ if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
+ flicker.assertWmVisibleRegion(pipApp) {
+ // first check against landscape bounds then against portrait bounds
+ coversAtMost(displayBounds).then().coversAtMost(
+ portraitDisplayBounds
+ )
+ }
+ } else {
+ // always check against the display bounds which do not change during transition
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 2f7a25ea586d..e38c4c34fc53 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -20,6 +20,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -72,7 +73,7 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
}
}
- @Presubmit
+ @FlakyTest(bugId = 293133362)
@Test
override fun pipLayerReduces() {
flicker.assertLayers {
@@ -84,7 +85,7 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
}
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
- @Presubmit
+ @FlakyTest(bugId = 255578530)
@Test
fun pipLayerMovesTowardsRightBottomCorner() {
// in gestural nav the swipe makes PiP first go upwards
@@ -107,4 +108,10 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
+
+ @FlakyTest(bugId = 289943985)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 68bc9a28967e..9256725c6180 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -21,7 +21,7 @@ import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,11 +49,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
+class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
deleted file mode 100644
index 7a668897fbbe..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) :
- ClosePipBySwipingDownTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
- * orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index dc48696f3197..002c019eff93 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,11 +49,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
+class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.closePipWindow(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
deleted file mode 100644
index 718b14babc4f..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) :
- ClosePipWithDismissButtonTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
- * orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 5e392628aa6a..6dd68b0d029d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -40,11 +40,10 @@ import org.junit.runners.Parameterized
* Press Home button or swipe up to go Home and put [pipApp] in pip mode
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
deleted file mode 100644
index 2b3e76a964c4..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/** This test will fail because of b/264261596 */
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) :
- EnterPipOnUserLeaveHintTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index ec35837bc8dd..8207b85c3e0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.app.Activity
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -28,13 +27,14 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
-import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
-import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
+import com.android.wm.shell.flicker.pip.common.PipTransition
+import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -65,11 +65,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
deleted file mode 100644
index 92642197e9be..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.common.flicker.assertions.FlickerTest
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/** This test fails because of b/264261596 */
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) :
- EnterPipToOtherOrientation(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 76c811cbbeea..cc943678d492 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -46,7 +46,6 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
deleted file mode 100644
index 78e80497747c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) :
- EnterPipViaAppUiButtonTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
- * orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index b80b7483ba4d..7da442901e40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -48,11 +48,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
+class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
deleted file mode 100644
index e25c0d6eddc0..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) :
- ExitPipToAppViaExpandButtonTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index f003ed8a77e0..0ad9e4c61d83 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -47,11 +47,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
+class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
deleted file mode 100644
index be19f3cd1970..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) :
- ExitPipToAppViaIntentTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
- * orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index a1d3a117482e..89a6c93e478c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -23,7 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,11 +51,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.doubleClickPipWindow(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
deleted file mode 100644
index 3095cac94598..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) :
- ExpandPipOnDoubleClickTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index 8c8d280aea9a..8978af0088b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -22,7 +22,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,11 +30,10 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/** Test expanding a pip window via pinch out gesture. */
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
deleted file mode 100644
index 1a1ce6823f3b..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) :
- ExpandPipOnPinchOpenTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 421ad757f76a..4776206724cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -21,6 +21,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition
import com.android.wm.shell.flicker.utils.Direction
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index dffc822e7aec..425cbfaffedd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -25,9 +25,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,11 +35,10 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
override val thisTransition: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
deleted file mode 100644
index 63292a4f2ca3..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.Rotation
-import android.tools.common.flicker.assertions.FlickerTest
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) :
- MovePipOnImeVisibilityChangeTest(flicker) {
- companion object {
- private const val TAG_IME_VISIBLE = "imeIsVisible"
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 992f1bc4ace3..18f30d96938b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -21,6 +21,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition
import com.android.wm.shell.flicker.utils.Direction
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 0c6fc5636a5b..c7f2786debd0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -23,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index de64f78a31eb..cabc1cc0b023 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -17,16 +17,17 @@
package com.android.wm.shell.flicker.pip
import android.graphics.Rect
-import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,6 +35,7 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/** Test the snapping of a PIP window via dragging, releasing, and checking its final location. */
+@FlakyTest(bugId = 294993100)
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -80,8 +82,8 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
/**
* Checks that the visible region area of [pipApp] moves to closest edge during the animation.
*/
- @Presubmit
@Test
+ @FlakyTest(bugId = 294993100)
fun pipLayerMovesToClosestEdge() {
flicker.assertLayers {
val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
@@ -95,6 +97,90 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
}
}
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun hasAtMostOnePipDismissOverlayWindow() {
+ super.hasAtMostOnePipDismissOverlayWindow()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 0295741bc441..6748626d4e46 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -23,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index c315e744bd55..1f69847e5481 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.app.Activity
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -27,10 +26,12 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
-import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.common.PipTransition
+import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 0ff9cfff873e..308ece40402f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -23,9 +23,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,11 +55,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
deleted file mode 100644
index 25164711b2e5..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.tools.common.flicker.assertions.FlickerTest
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
- * orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return LegacyFlickerTestFactory.rotationTests()
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
new file mode 100644
index 000000000000..c9a98c73e5e5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.apps
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import com.android.wm.shell.flicker.pip.common.EnterPipTransition
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ protected abstract val standardAppHelper: StandardAppHelper
+
+ /** Checks [standardAppHelper] window remains visible throughout the animation */
+ @Postsubmit
+ @Test
+ override fun pipAppWindowAlwaysVisible() {
+ flicker.assertWm { this.isAppWindowVisible(standardAppHelper.packageNameMatcher) }
+ }
+
+ /** Checks [standardAppHelper] layer remains visible throughout the animation */
+ @Postsubmit
+ @Test
+ override fun pipAppLayerAlwaysVisible() {
+ flicker.assertLayers { this.isVisible(standardAppHelper.packageNameMatcher) }
+ }
+
+ /** Checks the content overlay appears then disappears during the animation */
+ @Postsubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ super.pipOverlayLayerAppearThenDisappear()
+ }
+
+ /**
+ * Checks that [standardAppHelper] window remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Postsubmit
+ @Test
+ override fun pipWindowRemainInsideVisibleBounds() {
+ flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) {
+ coversAtMost(displayBounds)
+ }
+ }
+
+ /**
+ * Checks that the [standardAppHelper] layer remains inside the display bounds throughout the
+ * whole animation
+ */
+ @Postsubmit
+ @Test
+ override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
+ flicker.assertLayersVisibleRegion(
+ standardAppHelper.packageNameMatcher.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ ) {
+ coversAtMost(displayBounds)
+ }
+ }
+
+ /** Checks that the visible region of [standardAppHelper] always reduces during the animation */
+ @Postsubmit
+ @Test
+ override fun pipLayerReduces() {
+ flicker.assertLayers {
+ val pipLayerList = this.layers {
+ standardAppHelper.layerMatchesAnyOf(it) && it.isVisible
+ }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
+ }
+ }
+ }
+
+ /** Checks that [standardAppHelper] window becomes pinned */
+ @Postsubmit
+ @Test
+ override fun pipWindowBecomesPinned() {
+ flicker.assertWm {
+ invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper.packageNameMatcher) }
+ .then()
+ .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper.packageNameMatcher) }
+ }
+ }
+
+ /** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */
+ @Postsubmit
+ @Test
+ override fun launcherLayerBecomesVisible() {
+ super.launcherLayerBecomesVisible()
+ }
+
+ /**
+ * Checks that the focus changes between the [standardAppHelper] window and the launcher when
+ * closing the pip window
+ */
+ @Postsubmit
+ @Test
+ override fun focusChanges() {
+ flicker.assertEventLog {
+ this.focusChanges(standardAppHelper.packageName, "NexusLauncherActivity")
+ }
+ }
+
+ @Postsubmit
+ @Test
+ override fun hasAtMostOnePipDismissOverlayWindow() = super.hasAtMostOnePipDismissOverlayWindow()
+
+ // ICommonAssertions.kt overrides due to Morris overlay
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
+ */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ // this fails due to Morris overlay
+ }
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
+ * transition
+ */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ // this fails due to Morris overlay
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
+ *
+ * Note: Phones only
+ */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ // this fails due to Morris overlay
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition
+ */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
+ *
+ * Note: Large screen only
+ */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole
+ * transition
+ */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
+ * transition
+ */
+ @Postsubmit
+ @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ /** Checks that all parts of the screen are covered during the transition */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
new file mode 100644
index 000000000000..d7ba3d57b548
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.apps
+
+import android.content.Context
+import android.location.Criteria
+import android.location.Location
+import android.location.LocationManager
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.platform.test.annotations.Postsubmit
+import android.tools.device.apphelpers.MapsAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.RequiresDevice
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from Maps app by interacting with the app UI
+ *
+ * To run this test: `atest WMShellFlickerTests:MapsEnterPipTest`
+ *
+ * Actions:
+ * ```
+ * Launch Maps and start navigation mode
+ * Go home to enter PiP
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
+ override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)
+
+ val locationManager: LocationManager =
+ instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ val mainHandler = Handler(Looper.getMainLooper())
+ var mockLocationEnabled = false
+
+ val updateLocation = object : Runnable {
+ override fun run() {
+ // early bail out if mocking location is not enabled
+ if (!mockLocationEnabled) return
+ val location = Location("Googleplex")
+ location.latitude = 37.42243438411294
+ location.longitude = -122.08426281892311
+ location.time = System.currentTimeMillis()
+ location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
+ location.accuracy = 100f
+ locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
+ mainHandler.postDelayed(this, 5)
+ }
+ }
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ locationManager.addTestProvider(
+ LocationManager.GPS_PROVIDER,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ Criteria.POWER_LOW,
+ Criteria.ACCURACY_FINE
+ )
+ locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true)
+ mockLocationEnabled = true
+ mainHandler.post(updateLocation)
+
+ // normal app open through the Launcher All Apps
+ // var mapsAddressOption = "Golden Gate Bridge"
+ // standardAppHelper.open()
+ // standardAppHelper.doSearch(mapsAddressOption)
+ // standardAppHelper.getDirections()
+ // standardAppHelper.startNavigation();
+
+ standardAppHelper.launchViaIntent(
+ wmHelper,
+ MapsAppHelper.getMapIntent(MapsAppHelper.INTENT_NAVIGATION)
+ )
+
+ standardAppHelper.waitForNavigationToStart()
+ }
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ standardAppHelper.exit(wmHelper)
+ mainHandler.removeCallbacks(updateLocation)
+ // the main looper callback might have tried to provide a new location after the
+ // provider is no longer in test mode, causing a crash, this prevents it from happening
+ mockLocationEnabled = false
+ locationManager.removeTestProvider(LocationManager.GPS_PROVIDER)
+ }
+ }
+
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { tapl.goHome() }
+ }
+
+ @Postsubmit
+ @Test
+ override fun focusChanges() {
+ // in gestural nav the focus goes to different activity on swipe up with auto enter PiP
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ super.focusChanges()
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
new file mode 100644
index 000000000000..c370d91034fd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.apps
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.YouTubeAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.RequiresDevice
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from YouTube app by interacting with the app UI
+ *
+ * To run this test: `atest WMShellFlickerTests:YouTubeEnterPipTest`
+ *
+ * Actions:
+ * ```
+ * Launch YouTube and start playing a video
+ * Go home to enter PiP
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
+ override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ standardAppHelper.launchViaIntent(
+ wmHelper,
+ YouTubeAppHelper.getYoutubeVideoIntent("HPcEAtoXXLA"),
+ ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
+ )
+ standardAppHelper.waitForVideoPlaying()
+ }
+ }
+
+ override val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ standardAppHelper.exit(wmHelper)
+ }
+ }
+
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ transitions { tapl.goHome() }
+ }
+
+ @Postsubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // YouTube uses source rect hint, so PiP overlay is never present
+ }
+
+ @Postsubmit
+ @Test
+ override fun focusChanges() {
+ // in gestural nav the focus goes to different activity on swipe up with auto enter PiP
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ super.focusChanges()
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
index a17144b7cef3..751f2bc0807f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package com.android.wm.shell.flicker.pip.common
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
index 6d20740e239c..9c129e47efba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package com.android.wm.shell.flicker.pip.common
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -91,7 +91,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
/** Checks that [pipApp] window becomes pinned */
@Presubmit
@Test
- fun pipWindowBecomesPinned() {
+ open fun pipWindowBecomesPinned() {
flicker.assertWm {
invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp) }
.then()
@@ -102,7 +102,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
/** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */
@Presubmit
@Test
- fun launcherLayerBecomesVisible() {
+ open fun launcherLayerBecomesVisible() {
flicker.assertLayers {
isInvisible(ComponentNameMatcher.LAUNCHER)
.then()
@@ -117,7 +117,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
@Presubmit
@Test
open fun focusChanges() {
- flicker.assertEventLog { this.focusChanges(pipApp.`package`, "NexusLauncherActivity") }
+ flicker.assertEventLog { this.focusChanges(pipApp.packageName, "NexusLauncherActivity") }
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
index dfffba831dc3..9450bdd2d894 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package com.android.wm.shell.flicker.pip.common
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
index a8fb63de244b..7e42bc11a9c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package com.android.wm.shell.flicker.pip.common
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index 096af39488e9..7b7f1d7b5a82 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package com.android.wm.shell.flicker.pip.common
import android.app.Instrumentation
import android.content.Intent
@@ -92,7 +92,7 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
@Presubmit
@Test
- fun hasAtMostOnePipDismissOverlayWindow() {
+ open fun hasAtMostOnePipDismissOverlayWindow() {
val matcher = ComponentNameMatcher("", "pip-dismiss-overlay")
flicker.assertWm {
val overlaysPerState =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
index 000ae8f9458e..c6cbcd052fe0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
@@ -26,7 +26,7 @@ import com.android.server.wm.flicker.helpers.PipAppHelper
/** Helper class for PIP app on AndroidTV */
open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) {
- private val appSelector = By.pkg(`package`).depth(0)
+ private val appSelector = By.pkg(packageName).depth(0)
val ui: UiObject2?
get() = uiDevice.findObject(appSelector)
@@ -46,7 +46,7 @@ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instr
}
override fun clickObject(resId: String) {
- val selector = By.res(`package`, resId)
+ val selector = By.res(packageName, resId)
focusOnObject(selector) || error("Could not focus on `$resId` object")
uiDevice.pressDPadCenter()
}
@@ -68,7 +68,7 @@ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instr
}
fun waitUntilClosed(): Boolean {
- val appSelector = By.pkg(`package`).depth(0)
+ val appSelector = By.pkg(packageName).depth(0)
return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
index 610cedefe594..5f157856aa36 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service
+package com.android.wm.shell.flicker.service.common
import android.app.Instrumentation
import android.platform.test.rule.NavigationModeRule
@@ -23,6 +23,7 @@ import android.platform.test.rule.UnlockScreenRule
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.device.apphelpers.MessagingAppHelper
+import android.tools.device.flicker.rules.ArtifactSaverRule
import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.flicker.rules.LaunchAppRule
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
@@ -33,9 +34,10 @@ object Utils {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
- return RuleChain.outerRule(UnlockScreenRule())
+ return RuleChain.outerRule(ArtifactSaverRule())
+ .around(UnlockScreenRule())
.around(
- NavigationModeRule(navigationMode.value, /* changeNavigationModeAfterTest */ false)
+ NavigationModeRule(navigationMode.value, false)
)
.around(
LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
new file mode 100644
index 000000000000..3ab6a1ee061d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
@@ -0,0 +1,2 @@
+# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 8cb25fe531b8..69499b9b488b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
index fa1be63296e0..bd627f4babaa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index aa35237b615f..a8f4d0a24c7e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index e195360cdc36..cee9bbfb4aa4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
@Test
override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 5f771c7545c7..169b5cfa3462 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 729a401fe34c..412c011a3f17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
- @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
@Test
override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 00f607343b3b..4ff0b4362e60 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
- @ExpectedScenarios(["QUICKSWITCH"])
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
@Test
override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index b3340e77dbfa..930f31d1f348 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith
class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
- @ExpectedScenarios(["QUICKSWITCH"])
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
@Test
override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 7cbc1c3c272c..c744103d49ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -16,7 +16,11 @@
package com.android.wm.shell.flicker.service.splitscreen.flicker
+import android.tools.common.flicker.FlickerConfig
import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
@@ -25,7 +29,14 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
- @ExpectedScenarios(["QUICKSWITCH"])
+ @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
@Test
override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index 2eb81e0d0492..11a4e02c5e37 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -16,7 +16,11 @@
package com.android.wm.shell.flicker.service.splitscreen.flicker
+import android.tools.common.flicker.FlickerConfig
import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
@@ -25,7 +29,14 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
- @ExpectedScenarios(["QUICKSWITCH"])
+ @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
@Test
override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index 92b62273d8cb..b444bad8322e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavPortraitBenchmark : CopyContentInSplit(Rotation.ROTATION_0) {
+open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 566adec75615..e2ab989af027 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index e6d56b5c94d3..22b81028dd1d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavLandscapeBenchmark :
+open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 6752c58bd568..3fb014f9aac9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavPortraitBenchmark :
+open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 7c9ab9939dd0..ea1f9426bb81 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark :
+open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 4b795713cb23..8f23a790081d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark :
+open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index 71ef48bea686..b0f39e592744 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 04950799732e..6ce874641c6f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavLandscapeBenchmark : DragDividerToResize(Rotation.ROTATION_90) {
+open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index c78729c6dc92..9f74edf0a79a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark :
+open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 30bce2f657b1..1e4055e2a50b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark :
+open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index b33ea7c89158..c3b8132f4a50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark :
+open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 07a86a57117b..7756d048efbb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark :
+open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index 9a1d12787b9d..c72aa5aaeca6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark :
+open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 266e268a3537..cc88f27b9045 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark :
+open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 83fc30bceb7b..87b38b133357 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark :
+open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index b2f19299c7f0..ca347ed25f08 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark :
+open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index dae92dddbfec..65978192e8e1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark :
+open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 732047ba38ad..6df31fc9419b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark :
+open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 1de7efd7970a..02596c5f6202 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark :
+open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 1a046aa5b09e..9d579f60eaaa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark :
+open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 6e88f0eddee8..da853420a705 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark :
+open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index d26a29c80583..1ae2c9eb8a1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark :
+open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 4a552b0aed6a..b1b562577acc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark :
+open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index b7376eaea66d..08c437eda71c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark :
+open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index b2d05e4a2632..efbf86d028ee 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark :
+open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 6de31b1315e4..f7072fae2e7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark :
+open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index aab18a6d27b9..d80d1120017e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark :
+open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index b074f2c161c9..30ec37a41dde 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Rule
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
+open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 840401c23a91..1e086d2f5a9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark : UnlockKeyguardToSplitScreen() {
+open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index c402aa4444d8..932f89217277 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+ @get:Rule
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index e530f6369609..80ab24ddf9ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -51,7 +51,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
index e9fc43746d27..4c391047e853 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
index 416692c37b34..f6d1afc05a5a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index 494a246d2f50..db5a32a382fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
index 369bdfc1103d..d7b306c3be23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -19,13 +19,15 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
+import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
@@ -46,11 +48,15 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
+ Assume.assumeTrue(tapl.isTablet)
tapl.goHome()
+
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
@@ -58,7 +64,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.launchedAppState.taskbar
.openAllApps()
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 776c397cc354..cc982d1ba860 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -19,13 +19,15 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
+import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
@@ -47,14 +49,18 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
+ Assume.assumeTrue(tapl.isTablet)
// Send a notification
sendNotificationApp.launchViaIntent(wmHelper)
sendNotificationApp.postNotification(wmHelper)
tapl.goHome()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 5d67dc7e231b..fa12bb869467 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -19,11 +19,12 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
@@ -49,12 +50,13 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
fun setup() {
Assume.assumeTrue(tapl.isTablet)
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
tapl.goHome()
SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
primaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
@@ -63,7 +65,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
.getAppIcon(secondaryApp.appName)
.openDeepShortcutMenu()
.getMenuItem("Split Screen Secondary Activity")
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
// TODO: Do we want this check in here? Add to the other tests?
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
index 5bbb42fd1864..2592fd40d902 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -23,9 +23,10 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
+import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
@@ -46,6 +47,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
+ Assume.assumeTrue(tapl.isTablet)
+
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
@@ -58,7 +61,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
open fun enterSplitScreenByDragFromTaskbar() {
tapl.launchedAppState.taskbar
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index c2100f641a55..983653b9b5ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -46,9 +46,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
- tapl.setEnableRotation(true)
- tapl.setExpectedRotation(rotation.value)
-
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
tapl.goHome()
@@ -57,11 +54,14 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
.withAppTransitionIdle()
.withHomeActivityVisible()
.waitForAndVerify()
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
}
@Test
open fun enterSplitScreenFromOverview() {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 70f3bed9afdc..068171d2e129 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -25,7 +25,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -48,11 +48,12 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- tapl.workspace.switchToOverview().dismissAllTasks()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 86f394da0231..64b75c5fd967 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -50,7 +50,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index d7b611e04d9d..179501089168 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index bf4c381b1c3a..7065846dc653 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -46,10 +46,12 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 4a9c32f10415..251cb50de017 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -51,8 +51,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
index 383a6b39a2b6..a9933bbe09fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 3702be9541a3..6b971699d212 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.EdgeExtensionComponentMatcher
@@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 8b906305506f..51588569a8aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByDividerBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 50f6a382a702..fc6c2b3d7ce7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index ca9c13009848..8b1689a9d816 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.DragDividerToResizeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
@@ -57,7 +57,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) :
@Test
fun splitScreenDividerKeepVisible() = flicker.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @Presubmit
+ @FlakyTest(bugId = 291678271)
@Test
fun primaryAppLayerVisibilityChanges() {
flicker.assertLayers {
@@ -69,7 +69,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) :
}
}
- @Presubmit
+ @FlakyTest(bugId = 291678271)
@Test
fun secondaryAppLayerVisibilityChanges() {
flicker.assertLayers {
@@ -87,7 +87,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) :
@Test
fun secondaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(secondaryApp)
- @FlakyTest(bugId = 245472831)
+ @FlakyTest(bugId = 291678271)
@Test
fun primaryAppBoundsChanges() {
flicker.splitAppLayerBoundsChanges(
@@ -97,7 +97,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) :
)
}
- @Presubmit
+ @FlakyTest(bugId = 291678271)
@Test
fun secondaryAppBoundsChanges() =
flicker.splitAppLayerBoundsChanges(
@@ -106,6 +106,12 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) :
portraitPosTop = true
)
+ @FlakyTest(bugId = 291678271)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index f8d1e1f1f498..99613f39060d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromAllAppsBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index ff5d93550541..756a7fa4ba98 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromNotificationBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index 7c710777087d..121b46acdb66 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromShortcutBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 83717062b05e..99deb9279271 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromTaskbarBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 0bfdbb4de7c5..212a4e3649dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenFromOverviewBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS
new file mode 100644
index 000000000000..3ab6a1ee061d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS
@@ -0,0 +1,2 @@
+# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 88bbc0e7880b..284c32ea110d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromAnotherAppBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index e85dc24a7781..9e6448f0bec9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromHomeBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index f7a9ed073002..8e28712cd993 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -16,13 +16,13 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromRecentBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 66f9b85ea572..fb0193b1830d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBetweenSplitPairsBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
new file mode 100644
index 000000000000..13875362a1c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.wm.shell.flicker.splitscreen.benchmark.SplitScreenBase
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import com.android.wm.shell.flicker.utils.layerBecomesInvisible
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsSnapToDivider
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switch between two split pairs.
+ *
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairsNoPip`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchBetweenSplitPairsNoPip(override val flicker: LegacyFlickerTest) :
+ SplitScreenBase(flicker) {
+
+ val thirdApp = SplitScreenUtils.getSendNotification(instrumentation)
+ val pipApp = PipAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ tapl.goHome()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
+ flicker.scenario.startRotation)
+ pipApp.enableAutoEnterForPipActivity()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ teardown {
+ pipApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ }
+ }
+
+ /** Checks that [pipApp] window won't enter pip */
+ @Presubmit
+ @Test
+ fun notEnterPip() {
+ flicker.assertWm { isNotPinned(pipApp) }
+ }
+
+ /** Checks the [pipApp] task did not reshow during transition. */
+ @Presubmit
+ @Test
+ fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(pipApp)
+ .then()
+ .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isInvisible(pipApp)
+ .isVisible(secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+
+ /** Checks the [pipApp] task become invisible after transition finish. */
+ @Presubmit @Test fun pipAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(pipApp)
+
+ /** Checks the [pipApp] task is in split screen bounds when transition start. */
+ @Presubmit
+ @Test
+ fun pipAppBoundsIsVisibleAtBegin() =
+ flicker.assertLayersStart {
+ this.splitAppLayerBoundsSnapToDivider(
+ pipApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true,
+ flicker.scenario.startRotation
+ )
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 851391d37323..f3145c97a6f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -17,12 +17,16 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark
import com.android.wm.shell.flicker.utils.ICommonAssertions
@@ -57,6 +61,22 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
}
@Test
+ @FlakyTest(bugId = 293578017)
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ // TODO(b/293578017) remove once that bug is resolve
+ @Test
+ @Presubmit
+ fun visibleLayersShownMoreThanOneConsecutiveEntry_withoutWallpaper() =
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(WALLPAPER_BBQ_WRAPPER)
+ )
+ }
+
+ @Test
fun splitScreenDividerIsVisibleAtEnd() {
flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index e5c1e75a75f4..3b9e53f9ce04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -40,7 +39,8 @@ abstract class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTe
protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ textEditApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.copyContentInSplit(
instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index e4e1af9d24ce..5fdde3ad23d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -36,7 +35,8 @@ abstract class DismissSplitScreenByDividerBenchmark(override val flicker: Legacy
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
if (tapl.isTablet) {
SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index b2dd02bf2c41..b7f6bfe7efd6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -36,7 +35,10 @@ abstract class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyF
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
+ flicker.scenario.startRotation)
+ }
transitions {
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index 078859166dbc..bb2a7aabed1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
@@ -38,7 +37,8 @@ abstract class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerT
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index 884e4513e893..394864ad9d4d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
@@ -48,7 +47,7 @@ abstract class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker:
tapl.launchedAppState.taskbar
.openAllApps()
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index e5c40b69726c..cd3fbab1497b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index 04510014c437..3b3be84f9841 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
@@ -54,7 +53,7 @@ abstract class EnterSplitScreenByDragFromShortcutBenchmark(
.getAppIcon(secondaryApp.appName)
.openDeepShortcutMenu()
.getMenuItem("Split Screen Secondary Activity")
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index 9e0ca1b20f09..eff355987cc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
@@ -47,7 +46,7 @@ abstract class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker:
transitions {
tapl.launchedAppState.taskbar
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index 06b4fe7e0eb4..5e5e7d7fc3c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -47,7 +46,7 @@ abstract class EnterSplitScreenFromOverviewBenchmark(override val flicker: Legac
.waitForAndVerify()
}
transitions {
- SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.splitFromOverview(tapl, device, flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
index 7ce995ac64aa..a0e437c25aa7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker.splitscreen.benchmark
import android.content.Context
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -33,7 +33,10 @@ abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(f
tapl.setEnableRotation(true)
setRotation(flicker.scenario.startRotation)
tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- tapl.workspace.switchToOverview().dismissAllTasks()
+ val overview = tapl.workspace.switchToOverview()
+ if (overview.hasTasks()) {
+ overview.dismissAllTasks()
+ }
}
}
@@ -43,11 +46,4 @@ abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(f
secondaryApp.exit(wmHelper)
}
}
-
- protected open val withoutTracing: FlickerBuilder.() -> Unit = {
- withoutLayerTracing()
- withoutWindowManagerTracing()
- withoutTransitionTracing()
- withoutTransactionsTracing()
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 007b7518b16e..46b0bd226daf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -25,7 +25,6 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -40,7 +39,8 @@ abstract class SwitchAppByDoubleTapDividerBenchmark(override val flicker: Legacy
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation) }
transitions {
SplitScreenUtils.doubleTapDividerToSwitch(device)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 10c8eebf7d1d..baf76932c7ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -40,7 +39,8 @@ abstract class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Le
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index a6e750fed70e..33b55f1a57d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -38,7 +37,8 @@ abstract class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFl
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index 7e8d5fb83157..b79dfb5f0665 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -38,7 +37,8 @@ abstract class SwitchBackToSplitFromRecentBenchmark(override val flicker: Legacy
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 56edad1ded19..0204d754585a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -40,8 +39,10 @@ abstract class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlic
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
+ secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
+ flicker.scenario.startRotation)
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 065d4d62be42..e71834de7123 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
index e5c124cbe775..f1cb37ee1293 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
@@ -265,6 +265,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
val dividerRegion =
layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region
?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found")
+ visibleRegion(component).isNotEmpty()
visibleRegion(component)
.coversAtMost(
if (displayBounds.width > displayBounds.height) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 3f8a1ae6bd79..6b3cfaf33c05 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.flicker.utils
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
@@ -41,6 +42,7 @@ import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
@@ -101,13 +103,15 @@ object SplitScreenUtils {
tapl: LauncherInstrumentation,
device: UiDevice,
primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ secondaryApp: StandardAppHelper,
+ rotation: Rotation
) {
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- splitFromOverview(tapl, device)
+ splitFromOverview(tapl, device, rotation)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
@@ -121,7 +125,7 @@ object SplitScreenUtils {
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+ fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice, rotation: Rotation) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
// split to left side.
@@ -129,7 +133,9 @@ object SplitScreenUtils {
// TAPL's currentTask on tablet is sometimes not what we expected if the overview
// contains more than 3 task views. We need to use uiautomator directly to find the
// second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
+ val home = tapl.workspace.switchToOverview()
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.overviewActions.clickSplit()
val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
@@ -145,9 +151,13 @@ object SplitScreenUtils {
}
snapshots[0].click()
} else {
- tapl.workspace
+ val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled()
+ tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview
+ val home = tapl.workspace
.switchToOverview()
- .currentTask
+ tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ home.currentTask
.tapMenu()
.tapSplitMenuItem()
.currentTask
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 17ed396987af..e7274918fa2b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -424,6 +424,7 @@ class PhysicsAnimatorTest : ShellTestCase() {
eq(-5f), anyFloat(), eq(true))
}
+ @Ignore("Started flaking despite no changes, tracking in b/299636216")
@Test
fun testIsPropertyAnimating() {
PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
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 3d8bd3854a45..e7d0f601ff5a 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
@@ -67,6 +67,7 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -85,12 +86,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
- private ShellInit mShellInit;
-
@Rule
public TestableContext mContext =
new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+ private ShellInit mShellInit;
@Mock
private IActivityTaskManager mActivityTaskManager;
@@ -116,6 +116,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
private TestableContentResolver mContentResolver;
private TestableLooper mTestableLooper;
+ private ShellBackAnimationRegistry mShellBackAnimationRegistry;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -126,11 +128,23 @@ public class BackAnimationControllerTest extends ShellTestCase {
ANIMATION_ENABLED);
mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
- mController = new BackAnimationController(mShellInit, mShellController,
- mShellExecutor, new Handler(mTestableLooper.getLooper()),
- mActivityTaskManager, mContext,
- mContentResolver, mAnimationBackground);
- mController.setEnableUAnimation(true);
+ mShellBackAnimationRegistry =
+ new ShellBackAnimationRegistry(
+ new CrossActivityAnimation(mContext, mAnimationBackground),
+ new CrossTaskBackAnimation(mContext, mAnimationBackground),
+ new CustomizeActivityAnimation(mContext, mAnimationBackground),
+ null);
+ mController =
+ new BackAnimationController(
+ mShellInit,
+ mShellController,
+ mShellExecutor,
+ new Handler(mTestableLooper.getLooper()),
+ mActivityTaskManager,
+ mContext,
+ mContentResolver,
+ mAnimationBackground,
+ mShellBackAnimationRegistry);
mShellInit.init();
mShellExecutor.flushAll();
}
@@ -138,12 +152,13 @@ public class BackAnimationControllerTest extends ShellTestCase {
private void createNavigationInfo(int backType,
boolean enableAnimation,
boolean isAnimationCallback) {
- BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
- .setType(backType)
- .setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
- .setOnBackInvokedCallback(mAppCallback)
- .setPrepareRemoteAnimation(enableAnimation)
- .setAnimationCallback(isAnimationCallback);
+ BackNavigationInfo.Builder builder =
+ new BackNavigationInfo.Builder()
+ .setType(backType)
+ .setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
+ .setOnBackInvokedCallback(mAppCallback)
+ .setPrepareRemoteAnimation(enableAnimation)
+ .setAnimationCallback(isAnimationCallback);
createNavigationInfo(builder);
}
@@ -188,18 +203,21 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Test
public void verifyNavigationFinishes() throws RemoteException {
- final int[] testTypes = new int[] {BackNavigationInfo.TYPE_RETURN_TO_HOME,
- BackNavigationInfo.TYPE_CROSS_TASK,
- BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- BackNavigationInfo.TYPE_DIALOG_CLOSE,
- BackNavigationInfo.TYPE_CALLBACK };
-
- for (int type: testTypes) {
+ final int[] testTypes =
+ new int[] {
+ BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ BackNavigationInfo.TYPE_CROSS_TASK,
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY,
+ BackNavigationInfo.TYPE_DIALOG_CLOSE,
+ BackNavigationInfo.TYPE_CALLBACK
+ };
+
+ for (int type : testTypes) {
registerAnimation(type);
}
- for (int type: testTypes) {
- final ResultListener result = new ResultListener();
+ for (int type : testTypes) {
+ final ResultListener result = new ResultListener();
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
@@ -275,10 +293,17 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Toggle the setting off
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
ShellInit shellInit = new ShellInit(mShellExecutor);
- mController = new BackAnimationController(shellInit, mShellController,
- mShellExecutor, new Handler(mTestableLooper.getLooper()),
- mActivityTaskManager, mContext,
- mContentResolver, mAnimationBackground);
+ mController =
+ new BackAnimationController(
+ shellInit,
+ mShellController,
+ mShellExecutor,
+ new Handler(mTestableLooper.getLooper()),
+ mActivityTaskManager,
+ mContext,
+ mContentResolver,
+ mAnimationBackground,
+ mShellBackAnimationRegistry);
shellInit.init();
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
@@ -398,17 +423,19 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Test
public void animationNotDefined() throws RemoteException {
- final int[] testTypes = new int[] {
- BackNavigationInfo.TYPE_RETURN_TO_HOME,
- BackNavigationInfo.TYPE_CROSS_TASK,
- BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- BackNavigationInfo.TYPE_DIALOG_CLOSE};
-
- for (int type: testTypes) {
+ final int[] testTypes =
+ new int[] {
+ BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ BackNavigationInfo.TYPE_CROSS_TASK,
+ BackNavigationInfo.TYPE_CROSS_ACTIVITY,
+ BackNavigationInfo.TYPE_DIALOG_CLOSE
+ };
+
+ for (int type : testTypes) {
unregisterAnimation(type);
}
- for (int type: testTypes) {
+ for (int type : testTypes) {
final ResultListener result = new ResultListener();
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
@@ -468,16 +495,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
public void testBackToActivity() throws RemoteException {
final CrossActivityAnimation animation = new CrossActivityAnimation(mContext,
mAnimationBackground);
- verifySystemBackBehavior(
- BackNavigationInfo.TYPE_CROSS_ACTIVITY, animation.mBackAnimationRunner);
+ verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY, animation.getRunner());
}
@Test
public void testBackToTask() throws RemoteException {
final CrossTaskBackAnimation animation = new CrossTaskBackAnimation(mContext,
mAnimationBackground);
- verifySystemBackBehavior(
- BackNavigationInfo.TYPE_CROSS_TASK, animation.mBackAnimationRunner);
+ verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_TASK, animation.getRunner());
}
private void verifySystemBackBehavior(int type, BackAnimationRunner animation)
@@ -554,10 +579,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
private static class ResultListener implements RemoteCallback.OnResultListener {
boolean mBackNavigationDone = false;
boolean mTriggerBack = false;
+
@Override
public void onResult(@Nullable Bundle result) {
mBackNavigationDone = true;
mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK);
}
- };
+ }
+ ;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
index e7d459893ce8..cebbbd890f05 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
@@ -102,15 +102,17 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
// start animation with remote animation targets
final CountDownLatch finishCalled = new CountDownLatch(1);
final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation(
- new RemoteAnimationTarget[]{close, open}, null, null, finishCallback);
+ mCustomizeActivityAnimation
+ .getRunner()
+ .startAnimation(
+ new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
eq(BOUND_SIZE), eq(BOUND_SIZE));
verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
eq(BOUND_SIZE), eq(BOUND_SIZE));
try {
- mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackInvoked();
+ mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
} catch (RemoteException r) {
fail("onBackInvoked throw remote exception");
}
@@ -133,15 +135,17 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
// start animation with remote animation targets
final CountDownLatch finishCalled = new CountDownLatch(1);
final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation(
- new RemoteAnimationTarget[]{close, open}, null, null, finishCallback);
+ mCustomizeActivityAnimation
+ .getRunner()
+ .startAnimation(
+ new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
eq(BOUND_SIZE), eq(BOUND_SIZE));
verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
eq(BOUND_SIZE), eq(BOUND_SIZE));
try {
- mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackCancelled();
+ mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled();
} catch (RemoteException r) {
fail("onBackCancelled throw remote exception");
}
@@ -155,11 +159,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
// start animation without any remote animation targets
final CountDownLatch finishCalled = new CountDownLatch(1);
final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation(
- new RemoteAnimationTarget[]{}, null, null, finishCallback);
+ mCustomizeActivityAnimation
+ .getRunner()
+ .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback);
try {
- mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackInvoked();
+ mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
} catch (RemoteException r) {
fail("onBackInvoked throw remote exception");
}
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 4a55429eacb6..26c73946c1c6 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
@@ -111,6 +111,8 @@ public class BubbleDataTest extends ShellTestCase {
@Mock
private BubbleLogger mBubbleLogger;
@Mock
+ private BubbleEducationController mEducationController;
+ @Mock
private ShellExecutor mMainExecutor;
@Captor
@@ -191,7 +193,7 @@ public class BubbleDataTest extends ShellTestCase {
mPositioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner,
+ mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
mMainExecutor);
// Used by BubbleData to set lastAccessedTime
@@ -385,6 +387,65 @@ public class BubbleDataTest extends ShellTestCase {
assertOverflowChangedTo(ImmutableList.of());
}
+ /**
+ * Verifies that the update shouldn't show the user education, if the education is not required
+ */
+ @Test
+ public void test_shouldNotShowEducation() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(false);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isFalse();
+ }
+
+ /**
+ * Verifies that the update should show the user education, if the education is required
+ */
+ @Test
+ public void test_shouldShowEducation() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(true);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isTrue();
+ }
+
+ /**
+ * Verifies that the update shouldn't show the user education, if the education is required but
+ * the bubble should auto-expand
+ */
+ @Test
+ public void test_shouldShowEducation_shouldAutoExpand() {
+ // Setup
+ when(mEducationController.shouldShowStackEducation(any())).thenReturn(true);
+ mBubbleData.setListener(mListener);
+ mBubbleA1.setShouldAutoExpand(true);
+
+ // Test
+ mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */
+ true);
+
+ // Verify
+ verifyUpdateReceived();
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.shouldShowEducation).isFalse();
+ }
+
// COLLAPSED / ADD
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
new file mode 100644
index 000000000000..f58332198696
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Handler
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.IWindowManager
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.statusbar.IStatusBarService
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.WindowManagerShellWrapper
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView
+import com.android.wm.shell.bubbles.properties.BubbleProperties
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.taskview.TaskViewTransitions
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for loading / inflating views & icons for a bubble.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class BubbleViewInfoTest : ShellTestCase() {
+
+ private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
+ private lateinit var iconFactory: BubbleIconFactory
+ private lateinit var bubble: Bubble
+
+ private lateinit var bubbleController: BubbleController
+ private lateinit var mainExecutor: ShellExecutor
+ private lateinit var bubbleStackView: BubbleStackView
+ private lateinit var bubbleBarLayerView: BubbleBarLayerView
+
+ @Before
+ fun setup() {
+ metadataFlagListener = Bubbles.BubbleMetadataFlagListener {}
+ iconFactory = BubbleIconFactory(context,
+ 60,
+ 30,
+ Color.RED,
+ mContext.resources.getDimensionPixelSize(
+ R.dimen.importance_ring_stroke_width))
+
+ mainExecutor = TestShellExecutor()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ val shellInit = ShellInit(mainExecutor)
+ val shellCommandHandler = ShellCommandHandler()
+ val shellController = ShellController(context, shellInit, shellCommandHandler,
+ mainExecutor)
+ val bubblePositioner = BubblePositioner(context, windowManager)
+ val bubbleData = BubbleData(context, mock<BubbleLogger>(), bubblePositioner,
+ BubbleEducationController(context), mainExecutor)
+ val surfaceSynchronizer = { obj: Runnable -> obj.run() }
+
+ bubbleController = BubbleController(
+ context,
+ shellInit,
+ shellCommandHandler,
+ shellController,
+ bubbleData,
+ surfaceSynchronizer,
+ FloatingContentCoordinator(),
+ mock<BubbleDataRepository>(),
+ mock<IStatusBarService>(),
+ windowManager,
+ WindowManagerShellWrapper(mainExecutor),
+ mock<UserManager>(),
+ mock<LauncherApps>(),
+ mock<BubbleLogger>(),
+ mock<TaskStackListenerImpl>(),
+ ShellTaskOrganizer(mainExecutor),
+ bubblePositioner,
+ mock<DisplayController>(),
+ null,
+ null,
+ mainExecutor,
+ mock<Handler>(),
+ mock<ShellExecutor>(),
+ mock<TaskViewTransitions>(),
+ mock<Transitions>(),
+ mock<SyncTransactionQueue>(),
+ mock<IWindowManager>(),
+ mock<BubbleProperties>())
+
+ bubbleStackView = BubbleStackView(context, bubbleController, bubbleData,
+ surfaceSynchronizer, FloatingContentCoordinator(), mainExecutor)
+ bubbleBarLayerView = BubbleBarLayerView(context, bubbleController)
+ }
+
+ @Test
+ fun testPopulate() {
+ bubble = createBubbleWithShortcut()
+ val info = BubbleViewInfoTask.BubbleViewInfo.populate(context,
+ bubbleController, bubbleStackView, iconFactory, bubble, false /* skipInflation */)
+ assertThat(info!!).isNotNull()
+
+ assertThat(info.imageView).isNotNull()
+ assertThat(info.expandedView).isNotNull()
+ assertThat(info.bubbleBarExpandedView).isNull()
+
+ assertThat(info.shortcutInfo).isNotNull()
+ assertThat(info.appName).isNotEmpty()
+ assertThat(info.rawBadgeBitmap).isNotNull()
+ assertThat(info.dotPath).isNotNull()
+ assertThat(info.bubbleBitmap).isNotNull()
+ assertThat(info.badgeBitmap).isNotNull()
+ }
+
+ @Test
+ fun testPopulateForBubbleBar() {
+ bubble = createBubbleWithShortcut()
+ val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
+ bubbleController, bubbleBarLayerView, iconFactory, bubble,
+ false /* skipInflation */)
+ assertThat(info!!).isNotNull()
+
+ assertThat(info.imageView).isNull()
+ assertThat(info.expandedView).isNull()
+ assertThat(info.bubbleBarExpandedView).isNotNull()
+
+ assertThat(info.shortcutInfo).isNotNull()
+ assertThat(info.appName).isNotEmpty()
+ assertThat(info.rawBadgeBitmap).isNotNull()
+ assertThat(info.dotPath).isNotNull()
+ assertThat(info.bubbleBitmap).isNotNull()
+ assertThat(info.badgeBitmap).isNotNull()
+ }
+
+ @Test
+ fun testPopulate_invalidShortcutIcon() {
+ bubble = createBubbleWithShortcut()
+
+ // This eventually calls down to load the shortcut icon from the app, simulate an
+ // exception here if the app has an issue loading the shortcut icon; we default to
+ // the app icon in that case / none of the icons will be null.
+ val mockIconFactory = mock<BubbleIconFactory>()
+ whenever(mockIconFactory.getBubbleDrawable(eq(context), eq(bubble.shortcutInfo),
+ any())).doThrow(RuntimeException())
+
+ val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
+ bubbleController, bubbleBarLayerView, iconFactory, bubble,
+ true /* skipInflation */)
+ assertThat(info).isNotNull()
+
+ assertThat(info?.shortcutInfo).isNotNull()
+ assertThat(info?.appName).isNotEmpty()
+ assertThat(info?.rawBadgeBitmap).isNotNull()
+ assertThat(info?.dotPath).isNotNull()
+ assertThat(info?.bubbleBitmap).isNotNull()
+ assertThat(info?.badgeBitmap).isNotNull()
+ }
+
+ private fun createBubbleWithShortcut(): Bubble {
+ val shortcutInfo = ShortcutInfo.Builder(mContext, "mockShortcutId").build()
+ return Bubble("mockKey", shortcutInfo, 1000, Resources.ID_NULL,
+ "mockTitle", 0 /* taskId */, "mockLocus", true /* isDismissible */,
+ mainExecutor, metadataFlagListener)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
new file mode 100644
index 000000000000..9655f97cb7cc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+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 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.when;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TransitionInfoBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests of {@link BubblesTransitionObserver}.
+ */
+@SmallTest
+public class BubblesTransitionObserverTest {
+
+ @Mock
+ private BubbleController mBubbleController;
+ @Mock
+ private BubbleData mBubbleData;
+
+ @Mock
+ private IBinder mTransition;
+ @Mock
+ private SurfaceControl.Transaction mStartT;
+ @Mock
+ private SurfaceControl.Transaction mFinishT;
+
+ @Mock
+ private Bubble mBubble;
+
+ private BubblesTransitionObserver mTransitionObserver;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTransitionObserver = new BubblesTransitionObserver(mBubbleController, mBubbleData);
+ }
+
+ @Test
+ public void testOnTransitionReady_open_collapsesStack() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_toFront_collapsesStack() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noTaskInfo_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Null task info
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, null /* taskInfo */);
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noTaskId_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Invalid task id
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT,
+ createTaskInfo(INVALID_TASK_ID));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_notOpening_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Transits that aren't opening
+ TransitionInfo info = createTransitionInfo(TRANSIT_CHANGE, createTaskInfo(2));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ info = createTransitionInfo(TRANSIT_CLOSE, createTaskInfo(3));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ info = createTransitionInfo(TRANSIT_TO_BACK, createTaskInfo(4));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_stackAnimating_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(true); // Stack is animating
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_stackNotExpanded_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(false); // Stack is not expanded
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noSelectedBubble_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(null); // No selected bubble
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_openingMatchesExpanded_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // What's moving to front is same as the opened bubble
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(1));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(int taskId) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ return taskInfo;
+ }
+
+ private TransitionInfo createTransitionInfo(int changeType,
+ ActivityManager.RunningTaskInfo info) {
+ final TransitionInfo.Change change = new TransitionInfo.Change(
+ new WindowContainerToken(mock(IWindowContainerToken.class)),
+ mock(SurfaceControl.class));
+ change.setMode(changeType);
+ change.setTaskInfo(info);
+
+ return new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(change).build();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 9f0d89bc3128..52375850b9a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -27,15 +27,13 @@ import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -66,7 +64,7 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
@Before
fun setup() {
- launcherApps = mock(LauncherApps::class.java)
+ launcherApps = mock<LauncherApps>()
repository = BubbleVolatileRepository(launcherApps)
}
@@ -98,7 +96,7 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
repository.addBubbles(user11.identifier, listOf(bubble12))
assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier))
- Mockito.verifyNoMoreInteractions(launcherApps)
+ verifyNoMoreInteractions(launcherApps)
}
@Test
@@ -167,9 +165,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -184,9 +181,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -200,9 +196,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble2, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -219,9 +214,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
user11.identifier))
assertThat(ret).isFalse() // bubbles were NOT removed
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
}
@Test
@@ -237,9 +231,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
assertThat(ret).isTrue() // bubbles were removed
assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
@@ -261,9 +254,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
// bubble2 is the work profile bubble and should be removed
assertThat(repository.getEntities(user0.identifier).toList())
.isEqualTo(listOf(bubble1, bubble3))
- verify(launcherApps, never()).uncacheShortcuts(anyString(),
- any(),
- any(UserHandle::class.java), anyInt())
+ verify(launcherApps, never())
+ .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>())
// User 11 bubbles should still be here
assertThat(repository.getEntities(user11.identifier).toList())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index fe2da5dd19fc..56d0f8e13f08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -19,6 +19,10 @@ package com.android.wm.shell.common.split;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -130,7 +134,7 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
public void testSetDivideRatio() {
mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
- mSplitLayout.setDivideRatio(0.5f);
+ mSplitLayout.setDivideRatio(SNAP_TO_50_50);
assertThat(mSplitLayout.getDividePosition()).isEqualTo(
mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
@@ -146,7 +150,7 @@ public class SplitLayoutTests extends ShellTestCase {
public void testSnapToDismissStart() {
// verify it callbacks properly when the snap target indicates dismissing split.
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
+ SNAP_TO_START_AND_DISMISS);
mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
@@ -158,7 +162,7 @@ public class SplitLayoutTests extends ShellTestCase {
public void testSnapToDismissEnd() {
// verify it callbacks properly when the snap target indicates dismissing split.
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
- DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
+ SNAP_TO_END_AND_DISMISS);
mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
deleted file mode 100644
index 605a762a395f..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ /dev/null
@@ -1,531 +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.wm.shell.desktopmode;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask;
-
-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.clearInvocations;
-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.when;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
-import android.window.DisplayAreaInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransaction.Change;
-import android.window.WindowContainerTransaction.HierarchyOp;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-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.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class DesktopModeControllerTest extends ShellTestCase {
-
- private static final int SECOND_DISPLAY = 2;
-
- @Mock
- private ShellController mShellController;
- @Mock
- private ShellTaskOrganizer mShellTaskOrganizer;
- @Mock
- private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- @Mock
- private ShellExecutor mTestExecutor;
- @Mock
- private Handler mMockHandler;
- @Mock
- private Transitions mTransitions;
- private DesktopModeController mController;
- private DesktopModeTaskRepository mDesktopModeTaskRepository;
- private ShellInit mShellInit;
- private StaticMockitoSession mMockitoSession;
-
- @Before
- public void setUp() {
- mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto1Enabled()).thenReturn(true);
- when(DesktopModeStatus.isActive(any())).thenReturn(true);
-
- mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
-
- mDesktopModeTaskRepository = new DesktopModeTaskRepository();
-
- mController = createController();
-
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>());
-
- mShellInit.init();
- clearInvocations(mShellTaskOrganizer);
- clearInvocations(mRootTaskDisplayAreaOrganizer);
- clearInvocations(mTransitions);
- }
-
- @After
- public void tearDown() {
- mMockitoSession.finishMocking();
- }
-
- @Test
- public void instantiate_addInitCallback() {
- verify(mShellInit).addInitCallback(any(), any());
- }
-
- @Test
- public void instantiate_flagOff_doNotAddInitCallback() {
- when(DesktopModeStatus.isProto1Enabled()).thenReturn(false);
- clearInvocations(mShellInit);
-
- createController();
-
- verify(mShellInit, never()).addInitCallback(any(), any());
- }
-
- @Test
- public void testDesktopModeEnabled_rootTdaSetToFreeform() {
- DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 1 change: Root TDA windowing mode
- assertThat(wct.getChanges().size()).isEqualTo(1);
- // Verify WCT has a change for setting windowing mode to freeform
- Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
- }
-
- @Test
- public void testDesktopModeDisabled_rootTdaSetToFullscreen() {
- DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
-
- mController.updateDesktopModeActive(false);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 1 change: Root TDA windowing mode
- assertThat(wct.getChanges().size()).isEqualTo(1);
- // Verify WCT has a change for setting windowing mode to fullscreen
- Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
- }
-
- @Test
- public void testDesktopModeEnabled_windowingModeCleared() {
- createMockDisplayArea();
- RunningTaskInfo freeformTask = createFreeformTask();
- RunningTaskInfo fullscreenTask = createFullscreenTask();
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(freeformTask, fullscreenTask, homeTask)));
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 2 changes: Root TDA windowing mode and 1 task
- assertThat(wct.getChanges().size()).isEqualTo(2);
- // No changes for tasks that are not standard or freeform
- assertThat(wct.getChanges().get(fullscreenTask.token.asBinder())).isNull();
- assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
- // Standard freeform task has windowing mode cleared
- Change change = wct.getChanges().get(freeformTask.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
- }
-
- @Test
- public void testDesktopModeDisabled_windowingModeAndBoundsCleared() {
- createMockDisplayArea();
- RunningTaskInfo freeformTask = createFreeformTask();
- RunningTaskInfo fullscreenTask = createFullscreenTask();
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(freeformTask, fullscreenTask, homeTask)));
-
- mController.updateDesktopModeActive(false);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 3 changes: Root TDA windowing mode and 2 tasks
- assertThat(wct.getChanges().size()).isEqualTo(3);
- // No changes to home task
- assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
- // Standard tasks have bounds cleared
- assertThatBoundsCleared(wct.getChanges().get(freeformTask.token.asBinder()));
- assertThatBoundsCleared(wct.getChanges().get(fullscreenTask.token.asBinder()));
- // Freeform standard tasks have windowing mode cleared
- assertThat(wct.getChanges().get(
- freeformTask.token.asBinder()).getWindowingMode()).isEqualTo(
- WINDOWING_MODE_UNDEFINED);
- }
-
- @Test
- public void testDesktopModeEnabled_homeTaskBehindVisibleTask() {
- createMockDisplayArea();
- RunningTaskInfo fullscreenTask1 = createFullscreenTask();
- fullscreenTask1.isVisible = true;
- RunningTaskInfo fullscreenTask2 = createFullscreenTask();
- fullscreenTask2.isVisible = false;
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(fullscreenTask1, fullscreenTask2, homeTask)));
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // Check that there are hierarchy changes for home task and visible task
- assertThat(wct.getHierarchyOps()).hasSize(2);
- // First show home task
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder());
-
- // Then visible task on top of it
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
- // Set up two active tasks on desktop, task2 is on top of task1.
- RunningTaskInfo freeformTask1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */);
- RunningTaskInfo freeformTask2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
- freeformTask1);
- when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
- freeformTask2);
-
- // Run show desktop apps logic
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Check wct has reorder calls
- assertThat(wct.getHierarchyOps()).hasSize(2);
-
- // Task 1 appeared first, must be first reorder to top.
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder());
-
- // Task 2 appeared last, must be last reorder to top.
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() {
- final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
- final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Check wct has reorder calls
- assertThat(wct.getHierarchyOps()).hasSize(2);
- // Task 1 appeared first, must be first reorder to top.
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder());
-
- // Task 2 appeared last, must be last reorder to top.
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_someAppsInvisible_reordersAll() {
- final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
- final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Both tasks should be reordered to top, even if one was already visible.
- assertThat(wct.getHierarchyOps()).hasSize(2);
- final HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder());
- final HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
- RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY);
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn(
- taskDefaultDisplay);
-
- RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY);
- mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn(
- taskSecondDisplay);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- assertThat(wct.getHierarchyOps()).hasSize(1);
- HierarchyOp op = wct.getHierarchyOps().get(0);
- assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder());
- }
-
- @Test
- public void testGetVisibleTaskCount_noTasks_returnsZero() {
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
- RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
-
- RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
-
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
- RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
-
- RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- false /* visible */);
-
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
- RunningTaskInfo taskDefaultDisplay = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY,
- taskDefaultDisplay.taskId,
- true /* visible */);
-
- RunningTaskInfo taskSecondDisplay = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY,
- taskSecondDisplay.taskId,
- true /* visible */);
-
- assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1);
- }
-
- @Test
- public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() {
- when(DesktopModeStatus.isActive(any())).thenReturn(false);
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_unsupportedTransit_returnsNull() {
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_notFreeform_returnsNull() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskOpen_returnsWct() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- WindowContainerTransaction wct = mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */));
- assertThat(wct).isNotNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskToFront_returnsWct() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- WindowContainerTransaction wct = mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
- assertThat(wct).isNotNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskOpen_doesNotStartAnotherTransition() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */));
- verifyZeroInteractions(mTransitions);
- }
-
- private DesktopModeController createController() {
- return new DesktopModeController(mContext, mShellInit, mShellController,
- mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
- mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
- }
-
- private DisplayAreaInfo createMockDisplayArea() {
- DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(),
- mContext.getDisplayId(), 0);
- when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
- .thenReturn(displayAreaInfo);
- return displayAreaInfo;
- }
-
- private WindowContainerTransaction getDesktopModeSwitchTransaction() {
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), arg.capture(), any());
- } else {
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
- }
- return arg.getValue();
- }
-
- private WindowContainerTransaction getBringAppsToFrontTransaction() {
- final ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- verify(mTransitions).startTransition(eq(TRANSIT_NONE), arg.capture(), any());
- } else {
- verify(mShellTaskOrganizer).applyTransaction(arg.capture());
- }
- return arg.getValue();
- }
-
- private void assertThatBoundsCleared(Change change) {
- assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue();
- assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
- }
-
-}
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 5d87cf8b25a6..c6cccc059e89 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
@@ -52,6 +52,8 @@ import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -94,6 +96,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
ToggleResizeDesktopTaskTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
+ @Mock lateinit var splitScreenController: SplitScreenController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -107,7 +110,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Before
fun setUp() {
mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
- whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true)
+ whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
shellInit = Mockito.spy(ShellInit(testExecutor))
desktopModeTaskRepository = DesktopModeTaskRepository()
@@ -116,6 +119,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
controller = createController()
+ controller.splitScreenController = splitScreenController
shellInit.init()
}
@@ -154,7 +158,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun instantiate_flagOff_doNotAddInitCallback() {
- whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false)
+ whenever(DesktopModeStatus.isEnabled()).thenReturn(false)
clearInvocations(shellInit)
createController()
@@ -341,6 +345,30 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToDesktop_splitTaskExitsSplit() {
+ var task = setUpSplitScreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
+ fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
+ var task = setUpFullscreenTask()
+ controller.moveToDesktop(desktopModeWindowDecoration, task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ )
+ }
+
+ @Test
fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
@@ -695,6 +723,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
return task
}
+ private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createSplitScreenTask(displayId)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
private fun markTaskVisible(task: RunningTaskInfo) {
desktopModeTaskRepository.updateVisibleFreeformTasks(
task.displayId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index 29a757c19d98..2f6f3207137d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.MockToken
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -45,12 +46,25 @@ class DesktopTestHelpers {
@JvmOverloads
fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setToken(MockToken().token())
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .setLastActiveTime(100)
- .build()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build()
+ }
+
+ /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */
+ @JvmStatic
+ @JvmOverloads
+ fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setLastActiveTime(100)
+ .build()
}
/** Create a new home task */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
index c40cd4069cab..45f6c8c7f69f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
@@ -23,7 +23,9 @@ import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN;
import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
-import static org.junit.Assert.assertTrue;
+import static java.util.Collections.EMPTY_LIST;
+
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -34,7 +36,6 @@ import android.graphics.drawable.Icon;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Log;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.PipMediaController;
@@ -46,7 +47,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Unit tests for {@link TvPipActionsProvider}
@@ -69,35 +72,38 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Mock
private PendingIntent mMockPendingIntent;
- private RemoteAction createRemoteAction(int identifier) {
+ private int mNumberOfRemoteActionsCreated = 0;
+
+ private RemoteAction createRemoteAction() {
+ final int identifier = mNumberOfRemoteActionsCreated++;
return new RemoteAction(mMockIcon, "" + identifier, "" + identifier, mMockPendingIntent);
}
private List<RemoteAction> createRemoteActions(int numberOfActions) {
List<RemoteAction> actions = new ArrayList<>();
for (int i = 0; i < numberOfActions; i++) {
- actions.add(createRemoteAction(i));
+ actions.add(createRemoteAction());
}
return actions;
}
- private boolean checkActionsMatch(List<TvPipAction> actions, int[] actionTypes) {
- for (int i = 0; i < actions.size(); i++) {
- int type = actions.get(i).getActionType();
- if (type != actionTypes[i]) {
- Log.e(TAG, "Action at index " + i + ": found " + type
- + ", expected " + actionTypes[i]);
- return false;
- }
- }
- return true;
+ private void assertActionTypes(List<Integer> expected, List<Integer> actual) {
+ assertEquals(getActionTypesStrings(expected), getActionTypesStrings(actual));
+ }
+
+ private static List<String> getActionTypesStrings(List<Integer> actionTypes) {
+ return actionTypes.stream().map(a -> TvPipAction.getActionTypeString(a))
+ .collect(Collectors.toList());
+ }
+
+ private List<Integer> getActionsTypes() {
+ return mActionsProvider.getActionsList().stream().map(a -> a.getActionType())
+ .collect(Collectors.toList());
}
@Before
public void setUp() {
- if (!isTelevision()) {
- return;
- }
+ assumeTelevision();
MockitoAnnotations.initMocks(this);
mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController,
mMockSystemActionsHandler);
@@ -105,57 +111,51 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void defaultSystemActions_regularPip() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
}
@Test
public void defaultSystemActions_expandedPip() {
- assumeTelevision();
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
}
@Test
public void expandedPip_enableExpansion_enable() {
- assumeTelevision();
// PiP has expanded PiP disabled.
- mActionsProvider.updateExpansionEnabled(false);
-
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 0, /* index= */ 3);
}
@Test
public void expandedPip_enableExpansion_disable() {
- assumeTelevision();
mActionsProvider.updateExpansionEnabled(true);
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(false);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 0, /* index= */ 3);
}
@Test
public void expandedPip_enableExpansion_AlreadyEnabled() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(true);
-
mActionsProvider.addListener(mMockListener);
mActionsProvider.updateExpansionEnabled(true);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
}
private void check_expandedPip_updateExpansionState(
@@ -167,8 +167,9 @@ public class TvPipActionProviderTest extends ShellTestCase {
mActionsProvider.addListener(mMockListener);
mActionsProvider.updatePipExpansionState(endExpansion);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE),
+ getActionsTypes());
if (updateExpected) {
verify(mMockListener).onActionsChanged(0, 1, 3);
@@ -180,7 +181,6 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void expandedPip_toggleExpansion_collapse() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ true,
/* endExpansion= */ false,
@@ -189,7 +189,6 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void expandedPip_toggleExpansion_expand() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ false,
/* endExpansion= */ true,
@@ -198,7 +197,6 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void expandedPiP_updateExpansionState_alreadyExpanded() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ true,
/* endExpansion= */ true,
@@ -207,7 +205,6 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void expandedPiP_updateExpansionState_alreadyCollapsed() {
- assumeTelevision();
check_expandedPip_updateExpansionState(
/* startExpansion= */ false,
/* endExpansion= */ false,
@@ -216,8 +213,6 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void regularPiP_updateExpansionState_setCollapsed() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.updatePipExpansionState(/* expanded= */ false);
mActionsProvider.addListener(mMockListener);
@@ -229,153 +224,207 @@ public class TvPipActionProviderTest extends ShellTestCase {
@Test
public void customActions_added() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(createRemoteActions(2), null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void customActions_replacedMore() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(createRemoteActions(3), null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_CUSTOM, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 2, /* index= */ 2);
}
@Test
public void customActions_replacedLess() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), null);
+ mActionsProvider.setAppActions(EMPTY_LIST, null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void customCloseAdded() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = new ArrayList<>();
mActionsProvider.setAppActions(customActions, null);
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(customActions, createRemoteAction(0));
+ mActionsProvider.setAppActions(customActions, createRemoteAction());
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
}
@Test
public void customClose_matchesOtherCustomAction() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = createRemoteActions(2);
- RemoteAction customClose = createRemoteAction(/* id= */ 10);
+ RemoteAction customClose = createRemoteAction();
customActions.add(customClose);
mActionsProvider.addListener(mMockListener);
mActionsProvider.setAppActions(customActions, customClose);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2);
}
@Test
public void mediaActions_added_whileCustomActionsExist() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.setAppActions(createRemoteActions(2), null);
mActionsProvider.addListener(mMockListener);
mActionsProvider.onMediaActionsChanged(createRemoteActions(3));
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener, times(0)).onActionsChanged(anyInt(), anyInt(), anyInt());
}
@Test
public void customActions_removed_whileMediaActionsExist() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.onMediaActionsChanged(createRemoteActions(2));
mActionsProvider.setAppActions(createRemoteActions(3), null);
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
+
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), null);
+ mActionsProvider.setAppActions(EMPTY_LIST, null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 2, /* index= */ 2);
}
@Test
public void customCloseOnly_mediaActionsShowing() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
mActionsProvider.onMediaActionsChanged(createRemoteActions(2));
mActionsProvider.addListener(mMockListener);
- mActionsProvider.setAppActions(createRemoteActions(0), createRemoteAction(5));
+ mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction());
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1);
}
@Test
public void customActions_showDisabledActions() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
-
List<RemoteAction> customActions = createRemoteActions(2);
customActions.get(0).setEnabled(false);
mActionsProvider.setAppActions(customActions, null);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
- ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
}
@Test
public void mediaActions_hideDisabledActions() {
- assumeTelevision();
- mActionsProvider.updateExpansionEnabled(false);
+ List<RemoteAction> customActions = createRemoteActions(2);
+ customActions.get(0).setEnabled(false);
+ mActionsProvider.onMediaActionsChanged(customActions);
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_mediaActions() {
List<RemoteAction> customActions = createRemoteActions(2);
customActions.get(0).setEnabled(false);
mActionsProvider.onMediaActionsChanged(customActions);
- assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
- new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE}));
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_customActions() {
+ List<RemoteAction> customActions = createRemoteActions(2);
+ customActions.get(0).setEnabled(false);
+ mActionsProvider.setAppActions(customActions, null);
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_customClose() {
+ mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction());
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
+ }
+
+ @Test
+ public void reset_All() {
+ mActionsProvider.setAppActions(createRemoteActions(2), createRemoteAction());
+ mActionsProvider.onMediaActionsChanged(createRemoteActions(3));
+
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE),
+ getActionsTypes());
+
+ mActionsProvider.reset();
+ assertActionTypes(
+ Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE),
+ getActionsTypes());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 82fe5f2f6afc..974539f23b80 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -53,9 +53,8 @@ public class TvPipGravityTest extends ShellTestCase {
@Before
public void setUp() {
- if (!isTelevision()) {
- return;
- }
+ assumeTelevision();
+
MockitoAnnotations.initMocks(this);
mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
mSizeSpecSource = new LegacySizeSpecSource(mContext, mPipDisplayLayoutState);
@@ -101,20 +100,22 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void regularPip_defaultGravity() {
- assumeTelevision();
checkGravity(mTvPipBoundsState.getDefaultGravity(), Gravity.RIGHT | Gravity.BOTTOM);
}
@Test
+ public void regularPip_defaultTvPipGravity() {
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), Gravity.RIGHT | Gravity.BOTTOM);
+ }
+
+ @Test
public void regularPip_defaultGravity_RTL() {
- assumeTelevision();
setRTL(true);
checkGravity(mTvPipBoundsState.getDefaultGravity(), Gravity.LEFT | Gravity.BOTTOM);
}
@Test
public void updateGravity_expand_vertical() {
- assumeTelevision();
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
@@ -130,7 +131,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_expand_horizontal() {
- assumeTelevision();
// Horizontal expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
@@ -146,7 +146,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_collapse() {
- assumeTelevision();
// Vertical expansion
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
assertGravityAfterCollapse(Gravity.CENTER_VERTICAL | Gravity.RIGHT,
@@ -164,7 +163,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_collapse_RTL() {
- assumeTelevision();
setRTL(true);
// Horizontal expansion
@@ -177,7 +175,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_expand_collapse() {
- assumeTelevision();
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
@@ -197,7 +194,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_expand_move_collapse() {
- assumeTelevision();
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
expandMoveCollapseCheck(Gravity.TOP | Gravity.RIGHT, KEYCODE_DPAD_LEFT,
@@ -230,7 +226,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_regular_valid() {
- assumeTelevision();
mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.RIGHT);
// clockwise
moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.LEFT, true);
@@ -246,7 +241,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_expanded_valid() {
- assumeTelevision();
mTvPipBoundsState.setTvPipExpanded(true);
// Vertical expanded PiP.
@@ -264,7 +258,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_regular_invalid() {
- assumeTelevision();
int gravity = Gravity.BOTTOM | Gravity.RIGHT;
mTvPipBoundsState.setTvPipGravity(gravity);
moveAndCheckGravity(KEYCODE_DPAD_DOWN, gravity, false);
@@ -288,7 +281,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_expanded_invalid() {
- assumeTelevision();
mTvPipBoundsState.setTvPipExpanded(true);
// Vertical expanded PiP.
@@ -318,7 +310,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void previousCollapsedGravity_defaultValue() {
- assumeTelevision();
assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
mTvPipBoundsState.getDefaultGravity());
setRTL(true);
@@ -328,7 +319,6 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void previousCollapsedGravity_changes_on_RTL() {
- assumeTelevision();
mTvPipBoundsState.setTvPipPreviousCollapsedGravity(Gravity.TOP | Gravity.LEFT);
setRTL(true);
assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
index 3a08d32bc430..e26dc7c10989 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
@@ -25,18 +25,24 @@ import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_NO_MENU;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+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 android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnWindowFocusChangeListener;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.SystemWindows;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -50,28 +56,38 @@ public class TvPipMenuControllerTest extends ShellTestCase {
@Mock
private SystemWindows mMockSystemWindows;
@Mock
- private SurfaceControl mMockPipLeash;
- @Mock
- private Handler mMockHandler;
- @Mock
- private TvPipActionsProvider mMockActionsProvider;
- @Mock
private TvPipMenuView mMockTvPipMenuView;
@Mock
private TvPipBackgroundView mMockTvPipBackgroundView;
+ private Handler mMainHandler;
private TvPipMenuController mTvPipMenuController;
+ private OnWindowFocusChangeListener mFocusChangeListener;
@Before
public void setUp() {
assumeTrue(isTelevision());
MockitoAnnotations.initMocks(this);
+ mMainHandler = new Handler(Looper.getMainLooper());
+
+ final ViewTreeObserver mockMenuTreeObserver = mock(ViewTreeObserver.class);
+ doReturn(mockMenuTreeObserver).when(mMockTvPipMenuView).getViewTreeObserver();
mTvPipMenuController = new TestTvPipMenuController();
mTvPipMenuController.setDelegate(mMockDelegate);
- mTvPipMenuController.setTvPipActionsProvider(mMockActionsProvider);
- mTvPipMenuController.attach(mMockPipLeash);
+ mTvPipMenuController.setTvPipActionsProvider(mock(TvPipActionsProvider.class));
+ mTvPipMenuController.attach(mock(SurfaceControl.class));
+ mFocusChangeListener = captureFocusChangeListener(mockMenuTreeObserver);
+ }
+
+ private OnWindowFocusChangeListener captureFocusChangeListener(
+ ViewTreeObserver mockTreeObserver) {
+ final ArgumentCaptor<OnWindowFocusChangeListener> focusChangeListenerCaptor =
+ ArgumentCaptor.forClass(OnWindowFocusChangeListener.class);
+ verify(mockTreeObserver).addOnWindowFocusChangeListener(
+ focusChangeListenerCaptor.capture());
+ return focusChangeListenerCaptor.getValue();
}
@Test
@@ -81,24 +97,25 @@ public class TvPipMenuControllerTest extends ShellTestCase {
@Test
public void testSwitch_FromNoMenuMode_ToMoveMode() {
- showAndAssertMoveMenu();
+ showAndAssertMoveMenu(true);
}
@Test
public void testSwitch_FromNoMenuMode_ToAllActionsMode() {
- showAndAssertAllActionsMenu();
+ showAndAssertAllActionsMenu(true);
}
@Test
public void testSwitch_FromMoveMode_ToAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ showAndAssertMoveMenu(true);
+ showAndAssertAllActionsMenu(false);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testSwitch_FromAllActionsMode_ToMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
}
@Test
@@ -110,187 +127,282 @@ public class TvPipMenuControllerTest extends ShellTestCase {
@Test
public void testCloseMenu_MoveMode() {
- showAndAssertMoveMenu();
+ showAndAssertMoveMenu(true);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testCloseMenu_AllActionsMode() {
- showAndAssertAllActionsMenu();
+ showAndAssertAllActionsMenu(true);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
+ }
+
+ @Test
+ public void testCloseMenu_MoveModeFollowedByMoveMode() {
+ showAndAssertMoveMenu(true);
+ showAndAssertMoveMenu(false);
+
+ closeMenuAndAssertMenuClosed(true);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
public void testCloseMenu_MoveModeFollowedByAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ showAndAssertMoveMenu(true);
+ showAndAssertAllActionsMenu(false);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
}
@Test
public void testCloseMenu_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
- closeMenuAndAssertMenuClosed();
+ closeMenuAndAssertMenuClosed(true);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_NoMenuMode() {
- mTvPipMenuController.onExitMoveMode();
+ public void testCloseMenu_AllActionsModeFollowedByAllActionsMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertAllActionsMenu(false);
+
+ closeMenuAndAssertMenuClosed(true);
+ verify(mMockDelegate, never()).onInMoveModeChanged();
+ }
+
+ @Test
+ public void testExitMenuMode_NoMenuMode() {
+ mTvPipMenuController.onExitCurrentMenuMode();
assertMenuIsOpen(false);
verify(mMockDelegate, never()).onMenuClosed();
+ verify(mMockDelegate, never()).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_MoveMode() {
- showAndAssertMoveMenu();
+ public void testExitMenuMode_MoveMode() {
+ showAndAssertMoveMenu(true);
- mTvPipMenuController.onExitMoveMode();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
assertMenuClosed();
verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testExitMoveMode_AllActionsMode() {
- showAndAssertAllActionsMenu();
-
- mTvPipMenuController.onExitMoveMode();
- assertMenuIsInAllActionsMode();
+ public void testExitMenuMode_AllActionsMode() {
+ showAndAssertAllActionsMenu(true);
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
}
@Test
- public void testExitMoveMode_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ public void testExitMenuMode_AllActionsModeFollowedByMoveMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertMoveMenu(false);
- mTvPipMenuController.onExitMoveMode();
- assertMenuIsInAllActionsMode();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ assertSwitchedToAllActionsMode(2);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
- verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
- }
- @Test
- public void testOnBackPress_NoMenuMode() {
- mTvPipMenuController.onBackPress();
- assertMenuIsOpen(false);
- verify(mMockDelegate, never()).onMenuClosed();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
}
@Test
- public void testOnBackPress_MoveMode() {
- showAndAssertMoveMenu();
+ public void testExitMenuMode_AllActionsModeFollowedByAllActionsMode() {
+ showAndAssertAllActionsMenu(true);
+ showAndAssertAllActionsMenu(false);
- pressBackAndAssertMenuClosed();
- verify(mMockDelegate, times(2)).onInMoveModeChanged();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+ verify(mMockDelegate, never()).onInMoveModeChanged();
}
@Test
- public void testOnBackPress_AllActionsMode() {
- showAndAssertAllActionsMenu();
-
- pressBackAndAssertMenuClosed();
- }
+ public void testExitMenuMode_MoveModeFollowedByAllActionsMode() {
+ showAndAssertMoveMenu(true);
- @Test
- public void testOnBackPress_MoveModeFollowedByAllActionsMode() {
- showAndAssertMoveMenu();
- showAndAssertAllActionsMenu();
+ showAndAssertAllActionsMenu(false);
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- pressBackAndAssertMenuClosed();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
}
@Test
- public void testOnBackPress_AllActionsModeFollowedByMoveMode() {
- showAndAssertAllActionsMenu();
- showAndAssertMoveMenu();
+ public void testExitMenuMode_MoveModeFollowedByMoveMode() {
+ showAndAssertMoveMenu(true);
+ showAndAssertMoveMenu(false);
- mTvPipMenuController.onBackPress();
- assertMenuIsInAllActionsMode();
+ mTvPipMenuController.onExitCurrentMenuMode();
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
verify(mMockDelegate, times(2)).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
- verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
-
- pressBackAndAssertMenuClosed();
}
@Test
public void testOnPipMovement_NoMenuMode() {
- assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+ moveAndAssertMoveSuccessful(false);
}
@Test
public void testOnPipMovement_MoveMode() {
- showAndAssertMoveMenu();
- assertPipMoveSuccessful(true, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
- verify(mMockDelegate).movePip(eq(TEST_MOVE_KEYCODE));
+ showAndAssertMoveMenu(true);
+ moveAndAssertMoveSuccessful(true);
}
@Test
public void testOnPipMovement_AllActionsMode() {
- showAndAssertAllActionsMenu();
- assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+ showAndAssertAllActionsMenu(true);
+ moveAndAssertMoveSuccessful(false);
}
@Test
- public void testOnPipWindowFocusChanged_NoMenuMode() {
- mTvPipMenuController.onPipWindowFocusChanged(false);
- assertMenuIsOpen(false);
+ public void testUnexpectedFocusChanges() {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
+
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed();
+
+ showAndAssertMoveMenu(true);
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed(2);
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
}
@Test
- public void testOnPipWindowFocusChanged_MoveMode() {
- showAndAssertMoveMenu();
- mTvPipMenuController.onPipWindowFocusChanged(false);
- assertMenuClosed();
+ public void testAsyncScenario_AllActionsModeRequestFollowedByAsyncMoveModeRequest() {
+ mTvPipMenuController.showMenu();
+ // Artificially delaying the focus change update and adding a move request to simulate an
+ // async problematic situation.
+ mTvPipMenuController.showMovementMenu();
+ // The first focus change update arrives
+ mFocusChangeListener.onWindowFocusChanged(true);
+
+ // We expect that the TvPipMenuController will directly switch to the "pending" menu mode
+ // - MODE_MOVE_MENU, because no change of focus is needed.
+ assertSwitchedToMoveMode();
+ }
+
+ @Test
+ public void testAsyncScenario_MoveModeRequestFollowedByAsyncAllActionsModeRequest() {
+ mTvPipMenuController.showMovementMenu();
+ mTvPipMenuController.showMenu();
+
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
+ verify(mMockDelegate, never()).onInMoveModeChanged();
}
@Test
- public void testOnPipWindowFocusChanged_AllActionsMode() {
- showAndAssertAllActionsMenu();
- mTvPipMenuController.onPipWindowFocusChanged(false);
+ public void testAsyncScenario_DropObsoleteIntermediateModeSwitchRequests() {
+ mTvPipMenuController.showMovementMenu();
+ mTvPipMenuController.closeMenu();
+
+ // Focus change from showMovementMenu() call.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToMoveMode();
+ verify(mMockDelegate).onInMoveModeChanged();
+
+ // Focus change from closeMenu() call.
+ mFocusChangeListener.onWindowFocusChanged(false);
assertMenuClosed();
+ verify(mMockDelegate, times(2)).onInMoveModeChanged();
+
+ // Unexpected focus gain should open MODE_ALL_ACTIONS_MENU.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(1);
+
+ mTvPipMenuController.closeMenu();
+ mTvPipMenuController.showMovementMenu();
+
+ assertSwitchedToMoveMode(2);
+
+ mFocusChangeListener.onWindowFocusChanged(false);
+ assertMenuClosed(2);
+
+ // Closing the menu resets the default menu mode, so the next focus gain opens the menu in
+ // the default mode - MODE_ALL_ACTIONS_MENU.
+ mFocusChangeListener.onWindowFocusChanged(true);
+ assertSwitchedToAllActionsMode(2);
+ verify(mMockDelegate, times(4)).onInMoveModeChanged();
+
}
- private void showAndAssertMoveMenu() {
+ private void showAndAssertMoveMenu(boolean focusChange) {
mTvPipMenuController.showMovementMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ }
+ assertSwitchedToMoveMode();
+ }
+
+ private void assertSwitchedToMoveMode() {
+ assertSwitchedToMoveMode(1);
+ }
+
+ private void assertSwitchedToMoveMode(int times) {
assertMenuIsInMoveMode();
- verify(mMockDelegate).onInMoveModeChanged();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_MOVE_MENU), eq(false));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_MOVE_MENU));
+ verify(mMockDelegate, times(2 * times - 1)).onInMoveModeChanged();
+ verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+ verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+ }
+
+ private void showAndAssertAllActionsMenu(boolean focusChange) {
+ showAndAssertAllActionsMenu(focusChange, 1);
}
- private void showAndAssertAllActionsMenu() {
+ private void showAndAssertAllActionsMenu(boolean focusChange, int times) {
mTvPipMenuController.showMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(true);
+ }
+
+ assertSwitchedToAllActionsMode(times);
+ }
+
+ private void assertSwitchedToAllActionsMode(int times) {
assertMenuIsInAllActionsMode();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(true));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+ verify(mMockTvPipMenuView, times(times))
+ .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+ verify(mMockTvPipBackgroundView, times(times))
+ .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
}
- private void closeMenuAndAssertMenuClosed() {
+ private void closeMenuAndAssertMenuClosed(boolean focusChange) {
mTvPipMenuController.closeMenu();
+ if (focusChange) {
+ mFocusChangeListener.onWindowFocusChanged(false);
+ }
assertMenuClosed();
}
- private void pressBackAndAssertMenuClosed() {
- mTvPipMenuController.onBackPress();
- assertMenuClosed();
+ private void moveAndAssertMoveSuccessful(boolean expectedSuccess) {
+ mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE);
+ verify(mMockDelegate, times(expectedSuccess ? 1 : 0)).movePip(eq(TEST_MOVE_KEYCODE));
}
private void assertMenuClosed() {
+ assertMenuClosed(1);
+ }
+
+ private void assertMenuClosed(int times) {
assertMenuIsOpen(false);
- verify(mMockDelegate).onMenuClosed();
- verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_NO_MENU), eq(false));
- verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_NO_MENU));
+ verify(mMockDelegate, times(times)).onMenuClosed();
+ verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
+ verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
}
private void assertMenuIsOpen(boolean open) {
@@ -312,15 +424,10 @@ public class TvPipMenuControllerTest extends ShellTestCase {
assertMenuIsOpen(true);
}
- private void assertPipMoveSuccessful(boolean expected, boolean actual) {
- assertTrue("Should " + (expected ? "" : "not ") + "move PiP when the menu is in mode "
- + mTvPipMenuController.getMenuModeString(), expected == actual);
- }
-
private class TestTvPipMenuController extends TvPipMenuController {
TestTvPipMenuController() {
- super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMockHandler);
+ super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMainHandler);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
index baa06f2f0c45..bbd65be9abda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -24,6 +24,7 @@ import android.window.IWindowContainerToken
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
import com.android.wm.shell.util.GroupedRecentTaskInfo
import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
@@ -123,6 +124,7 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+ assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_50_50)
}
@Test
@@ -156,7 +158,7 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
val task1 = createTaskInfo(id = 1)
val task2 = createTaskInfo(id = 2)
- val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_50_50)
return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 2c69522413d5..10e9e11e9004 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -17,10 +17,12 @@
package com.android.wm.shell.recents;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
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.mockitoSession;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -172,10 +174,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
// Verify only one update if the split info is the same
SplitBounds bounds1 = new SplitBounds(new Rect(0, 0, 50, 50),
- new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
SplitBounds bounds2 = new SplitBounds(new Rect(0, 0, 50, 50),
- new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
}
@@ -206,8 +208,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
- SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+ SplitBounds pair2Bounds =
+ new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -235,8 +239,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
- SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50);
+ SplitBounds pair2Bounds =
+ new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -256,7 +262,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto2Enabled()).thenReturn(true);
+ when(DesktopModeStatus.isEnabled()).thenReturn(true);
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -296,7 +302,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Disabled_doNotGroupFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto2Enabled()).thenReturn(false);
+ when(DesktopModeStatus.isEnabled()).thenReturn(false);
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -333,7 +339,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3);
// Add a pair
- SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 3);
+ SplitBounds pair1Bounds =
+ new SplitBounds(new Rect(), new Rect(), 2, 3, SNAP_TO_50_50);
mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
reset(mRecentTasksController);
@@ -367,6 +374,37 @@ public class RecentTasksControllerTest extends ShellTestCase {
verify(mRecentTasksController).notifyRecentTasksChanged();
}
+ @Test
+ public void getNullSplitBoundsNonSplitTask() {
+ SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3);
+ assertNull("splitBounds should be null for non-split task", sb);
+ }
+
+ @Test
+ public void getNullSplitBoundsInvalidTask() {
+ SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(INVALID_TASK_ID);
+ assertNull("splitBounds should be null for invalid taskID", sb);
+ }
+
+ @Test
+ public void getSplitBoundsForSplitTask() {
+ SplitBounds pair1Bounds = mock(SplitBounds.class);
+ SplitBounds pair2Bounds = mock(SplitBounds.class);
+
+ mRecentTasksController.addSplitPair(1, 2, pair1Bounds);
+ mRecentTasksController.addSplitPair(4, 3, pair2Bounds);
+
+ SplitBounds splitBounds2 = mRecentTasksController.getSplitBoundsForTaskId(2);
+ SplitBounds splitBounds1 = mRecentTasksController.getSplitBoundsForTaskId(1);
+ assertEquals("Different splitBounds for same pair", splitBounds1, splitBounds2);
+ assertEquals(splitBounds1, pair1Bounds);
+
+ SplitBounds splitBounds3 = mRecentTasksController.getSplitBoundsForTaskId(3);
+ SplitBounds splitBounds4 = mRecentTasksController.getSplitBoundsForTaskId(4);
+ assertEquals("Different splitBounds for same pair", splitBounds3, splitBounds4);
+ assertEquals(splitBounds4, pair2Bounds);
+ }
+
/**
* Helper to create a task with a given task id.
*/
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
index 50d02ae0dccd..b790aee6fb0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
@@ -1,5 +1,7 @@
package com.android.wm.shell.recents;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,21 +46,21 @@ public class SplitBoundsTest extends ShellTestCase {
@Test
public void testVerticalStacked() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
assertTrue(ssb.appsStackedVertically);
}
@Test
public void testHorizontalStacked() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
assertFalse(ssb.appsStackedVertically);
}
@Test
public void testHorizontalDividerBounds() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(0, dividerBounds.left);
assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top);
@@ -69,7 +71,7 @@ public class SplitBoundsTest extends ShellTestCase {
@Test
public void testVerticalDividerBounds() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
assertEquals(0, dividerBounds.top);
@@ -80,7 +82,7 @@ public class SplitBoundsTest extends ShellTestCase {
@Test
public void testEqualVerticalTaskPercent() {
SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
}
@@ -88,7 +90,7 @@ public class SplitBoundsTest extends ShellTestCase {
@Test
public void testEqualHorizontalTaskPercent() {
SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
- TASK_ID_1, TASK_ID_2);
+ TASK_ID_1, TASK_ID_2, SNAP_TO_50_50);
float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 568db919818c..99cd4f391153 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -218,8 +219,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
- public void startIntent_multiInstancesSupported_startTaskInBackgroundBeforeSplitActivated() {
- doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ public void startIntent_multiInstancesNotSupported_startTaskInBackgroundBeforeSplitActivated() {
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
@@ -237,6 +237,8 @@ public class SplitScreenControllerTests extends ShellTestCase {
verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull());
+ verify(mSplitScreenController, never()).supportMultiInstancesSplit(any());
+ verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 03ed18c86608..050443914355 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -65,12 +65,6 @@ public class TaskViewTransitionsTest extends ShellTestCase {
ActivityManager.RunningTaskInfo mTaskInfo;
@Mock
WindowContainerToken mToken;
- @Mock
- TaskViewTaskController mTaskViewTaskController2;
- @Mock
- ActivityManager.RunningTaskInfo mTaskInfo2;
- @Mock
- WindowContainerToken mToken2;
TaskViewTransitions mTaskViewTransitions;
@@ -86,16 +80,10 @@ public class TaskViewTransitionsTest extends ShellTestCase {
mTaskInfo.token = mToken;
mTaskInfo.taskId = 314;
mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
- when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
-
- mTaskInfo2 = new ActivityManager.RunningTaskInfo();
- mTaskInfo2.token = mToken2;
- mTaskInfo2.taskId = 315;
- mTaskInfo2.taskDescription = mock(ActivityManager.TaskDescription.class);
- when(mTaskViewTaskController2.getTaskInfo()).thenReturn(mTaskInfo2);
mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
mTaskViewTransitions.addTaskView(mTaskViewTaskController);
+ when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
}
@Test
@@ -138,7 +126,7 @@ public class TaskViewTransitionsTest extends ShellTestCase {
}
@Test
- public void testSetTaskBounds_taskVisibleWithPendingOpen_noTransaction() {
+ public void testSetTaskBounds_taskVisibleWithPending_noTransaction() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
@@ -154,43 +142,6 @@ public class TaskViewTransitionsTest extends ShellTestCase {
}
@Test
- public void testSetTaskBounds_taskVisibleWithPendingChange_transition() {
- assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
-
- mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
-
- // Consume the pending transition from visibility change
- TaskViewTransitions.PendingTransition pending =
- mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
- assertThat(pending).isNotNull();
- mTaskViewTransitions.startAnimation(pending.mClaimed,
- mock(TransitionInfo.class),
- new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(),
- mock(Transitions.TransitionFinishCallback.class));
- // Verify it was consumed
- TaskViewTransitions.PendingTransition checkPending =
- mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
- assertThat(checkPending).isNull();
-
- // Test that set bounds creates a new transition
- mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
- new Rect(0, 0, 100, 100));
- assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
- .isNotNull();
-
- // Test that set bounds again (with different bounds) creates another transition
- mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
- new Rect(0, 0, 300, 200));
- List<TaskViewTransitions.PendingTransition> pendingList =
- mTaskViewTransitions.findAllPending(mTaskViewTaskController)
- .stream()
- .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE)
- .toList();
- assertThat(pendingList.size()).isEqualTo(2);
- }
-
- @Test
public void testSetTaskBounds_sameBounds_noTransaction() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
@@ -217,16 +168,6 @@ public class TaskViewTransitionsTest extends ShellTestCase {
mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
assertThat(pendingBounds).isNotNull();
- // Test that setting same bounds with in-flight transition doesn't cause another one
- mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
- new Rect(0, 0, 100, 100));
- List<TaskViewTransitions.PendingTransition> pendingList =
- mTaskViewTransitions.findAllPending(mTaskViewTaskController)
- .stream()
- .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE)
- .toList();
- assertThat(pendingList.size()).isEqualTo(1);
-
// Consume the pending bounds transaction
mTaskViewTransitions.startAnimation(pendingBounds.mClaimed,
mock(TransitionInfo.class),
@@ -246,42 +187,6 @@ public class TaskViewTransitionsTest extends ShellTestCase {
assertThat(pendingBounds2).isNull();
}
-
- @Test
- public void testSetTaskBounds_taskVisibleWithDifferentTaskViewPendingChange_transition() {
- assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
-
- mTaskViewTransitions.addTaskView(mTaskViewTaskController2);
-
- mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
-
- // Consume the pending transition from visibility change
- TaskViewTransitions.PendingTransition pending =
- mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
- assertThat(pending).isNotNull();
- mTaskViewTransitions.startAnimation(pending.mClaimed,
- mock(TransitionInfo.class),
- new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(),
- mock(Transitions.TransitionFinishCallback.class));
- // Verify it was consumed
- TaskViewTransitions.PendingTransition checkPending =
- mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
- assertThat(checkPending).isNull();
-
- // Set the second taskview as visible & check that it has a pending transition
- mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController2, true);
- TaskViewTransitions.PendingTransition pending2 =
- mTaskViewTransitions.findPending(mTaskViewTaskController2, TRANSIT_TO_FRONT);
- assertThat(pending2).isNotNull();
-
- // Test that set bounds on the first taskview will create a new transition
- mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
- new Rect(0, 0, 100, 100));
- assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
- .isNotNull();
- }
-
@Test
public void testSetTaskVisibility_taskRemoved_noNPE() {
mTaskViewTransitions.removeTaskView(mTaskViewTaskController);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 99a1ac663286..a57a7bf4c659 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -99,6 +99,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -1061,7 +1062,8 @@ public class ShellTransitionTests extends ShellTestCase {
mTransactionPool, createTestDisplayController(), mMainExecutor,
mMainHandler, mAnimExecutor);
final RecentsTransitionHandler recentsHandler =
- new RecentsTransitionHandler(shellInit, transitions, null);
+ new RecentsTransitionHandler(shellInit, transitions,
+ mock(RecentTasksController.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
deleted file mode 100644
index 596d6dd3a3d2..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ /dev/null
@@ -1,318 +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.wm.shell.windowdecor;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-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_UNDEFINED;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.InputMonitor;
-import android.view.SurfaceControl;
-import android.view.SurfaceView;
-
-import androidx.test.filters.SmallTest;
-
-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.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopModeController;
-import com.android.wm.shell.desktopmode.DesktopTasksController;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/** Tests of {@link DesktopModeWindowDecorViewModel} */
-@SmallTest
-public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
-
- private static final String TAG = "DesktopModeWindowDecorViewModelTests";
- private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0);
-
- @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
- @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
-
- @Mock private Handler mMainHandler;
- @Mock private Choreographer mMainChoreographer;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
- @Mock private DisplayLayout mDisplayLayout;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private DesktopModeController mDesktopModeController;
- @Mock private DesktopTasksController mDesktopTasksController;
- @Mock private InputMonitor mInputMonitor;
- @Mock private InputManager mInputManager;
- @Mock private Transitions mTransitions;
- @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
- @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
- @Mock private SurfaceControl.Transaction mTransaction;
- @Mock private Display mDisplay;
- @Mock private ShellController mShellController;
- @Mock private ShellInit mShellInit;
- @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
- mDesktopModeKeyguardChangeListener;
- private final List<InputManager> mMockInputManagers = new ArrayList<>();
-
- private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
-
- @Before
- public void setUp() {
- mMockInputManagers.add(mInputManager);
-
- mDesktopModeWindowDecorViewModel =
- new DesktopModeWindowDecorViewModel(
- mContext,
- mMainHandler,
- mMainChoreographer,
- mShellInit,
- mTaskOrganizer,
- mDisplayController,
- mShellController,
- mSyncQueue,
- mTransitions,
- Optional.of(mDesktopModeController),
- Optional.of(mDesktopTasksController),
- mDesktopModeWindowDecorFactory,
- mMockInputMonitorFactory,
- mTransactionFactory,
- mDesktopModeKeyguardChangeListener
- );
-
- doReturn(mDesktopModeWindowDecoration)
- .when(mDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), any(), any(), any(), any(), any());
- doReturn(mTransaction).when(mTransactionFactory).get();
- doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
- doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
- doNothing().when(mShellController).addKeyguardChangeListener(any());
-
- when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
- // InputChannel cannot be mocked because it passes to InputEventReceiver.
- final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
- inputChannels[0].dispose();
- when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
-
- mDesktopModeWindowDecoration.mDisplay = mDisplay;
- doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
- }
-
- @Test
- public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory)
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue);
- verify(mDesktopModeWindowDecoration).close();
- }
-
- @Test
- public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED);
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
-
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, times(1))
- .create(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- surfaceControl,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue);
- }
-
- @Test
- public void testCreateAndDisposeEventReceiver() throws Exception {
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- runOnMainThread(() -> {
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- });
- verify(mMockInputMonitorFactory).create(any(), any());
- verify(mInputMonitor).dispose();
- }
-
- @Test
- public void testEventReceiversOnMultipleDisplays() throws Exception {
- runOnMainThread(() -> {
- SurfaceView surfaceView = new SurfaceView(mContext);
- final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
- final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay(
- "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400,
- /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- try {
- int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo secondTaskInfo =
- createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
- final ActivityManager.RunningTaskInfo thirdTaskInfo =
- createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
-
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT,
- finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl,
- startT, finishT);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo);
- mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
- } finally {
- secondaryDisplay.release();
- }
- });
- verify(mMockInputMonitorFactory, times(2)).create(any(), any());
- verify(mInputMonitor, times(1)).dispose();
- }
-
- @Test
- public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
- doReturn(true).when(
- mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
-
- final int taskId = 1;
- final ActivityManager.RunningTaskInfo taskInfo =
- createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
- taskInfo.isFocused = true;
- SurfaceControl surfaceControl = mock(SurfaceControl.class);
- runOnMainThread(() -> {
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
-
- mDesktopModeWindowDecorViewModel.onTaskOpening(
- taskInfo, surfaceControl, startT, finishT);
-
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- mDesktopModeWindowDecorViewModel.onTaskChanging(
- taskInfo, surfaceControl, startT, finishT);
- });
- verify(mDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), any(), any(), any(), any(), any());
- }
-
- private void runOnMainThread(Runnable r) throws Exception {
- final Handler mainHandler = new Handler(Looper.getMainLooper());
- final CountDownLatch latch = new CountDownLatch(1);
- mainHandler.post(() -> {
- r.run();
- latch.countDown();
- });
- latch.await(1, TimeUnit.SECONDS);
- }
-
- private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
- int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
- ActivityManager.RunningTaskInfo taskInfo =
- new TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setVisible(true)
- .build();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
- return taskInfo;
- }
-}
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
new file mode 100644
index 000000000000..00d70a75837b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -0,0 +1,344 @@
+/*
+ * 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.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.os.Handler
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.InputChannel
+import android.view.InputMonitor
+import android.view.SurfaceControl
+import android.view.SurfaceView
+import androidx.core.content.getSystemService
+import androidx.test.filters.SmallTest
+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.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.sysui.KeyguardChangeListener
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import java.util.Optional
+import java.util.function.Supplier
+
+/** Tests of [DesktopModeWindowDecorViewModel] */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+ @Mock private lateinit var mockDesktopModeWindowDecorFactory:
+ DesktopModeWindowDecoration.Factory
+ @Mock private lateinit var mockMainHandler: Handler
+ @Mock private lateinit var mockMainChoreographer: Choreographer
+ @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var mockDisplayController: DisplayController
+ @Mock private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock private lateinit var mockSyncQueue: SyncTransactionQueue
+ @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+ @Mock private lateinit var mockInputMonitor: InputMonitor
+ @Mock private lateinit var mockTransitions: Transitions
+ @Mock private lateinit var mockInputMonitorFactory:
+ DesktopModeWindowDecorViewModel.InputMonitorFactory
+ @Mock private lateinit var mockShellController: ShellController
+ @Mock private lateinit var mockShellExecutor: ShellExecutor
+ @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler
+
+ private val transactionFactory = Supplier<SurfaceControl.Transaction> {
+ SurfaceControl.Transaction()
+ }
+
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
+
+ @Before
+ fun setUp() {
+ shellInit = ShellInit(mockShellExecutor)
+ desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
+ mContext,
+ mockMainHandler,
+ mockMainChoreographer,
+ shellInit,
+ mockTaskOrganizer,
+ mockDisplayController,
+ mockShellController,
+ mockSyncQueue,
+ mockTransitions,
+ Optional.of(mockDesktopTasksController),
+ mockRecentsTransitionHandler,
+ mockDesktopModeWindowDecorFactory,
+ mockInputMonitorFactory,
+ transactionFactory,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
+ whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
+
+ // InputChannel cannot be mocked because it passes to InputEventReceiver.
+ val inputChannels = InputChannel.openInputChannelPair(TAG)
+ inputChannels.first().dispose()
+ whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1])
+
+ shellInit.init()
+ }
+
+ @Test
+ fun testDeleteCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task, taskSurface)
+
+ verify(mockDesktopModeWindowDecorFactory).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ verify(decoration).close()
+ }
+
+ @Test
+ fun testCreateCaptionOnChangeTransitionWhenNecessary() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_UNDEFINED,
+ activityType = ACTIVITY_TYPE_UNDEFINED
+ )
+ val taskSurface = SurfaceControl()
+ setUpMockDecorationForTask(task)
+
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, never()).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+
+ task.setWindowingMode(WINDOWING_MODE_FREEFORM)
+ task.setActivityType(ACTIVITY_TYPE_STANDARD)
+ onTaskChanging(task, taskSurface)
+ verify(mockDesktopModeWindowDecorFactory, times(1)).create(
+ mContext,
+ mockDisplayController,
+ mockTaskOrganizer,
+ task,
+ taskSurface,
+ mockMainHandler,
+ mockMainChoreographer,
+ mockSyncQueue,
+ mockRootTaskDisplayAreaOrganizer
+ )
+ }
+
+ @Test
+ fun testCreateAndDisposeEventReceiver() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+ verify(mockInputMonitorFactory).create(any(), any())
+ verify(mockInputMonitor).dispose()
+ }
+
+ @Test
+ fun testEventReceiversOnMultipleDisplays() {
+ val secondaryDisplay = createVirtualDisplay() ?: return
+ val secondaryDisplayId = secondaryDisplay.display.displayId
+ val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM)
+ val secondTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val thirdTask = createTask(
+ displayId = secondaryDisplayId,
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+ onTaskOpening(task)
+ onTaskOpening(secondTask)
+ onTaskOpening(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+ secondaryDisplay.release()
+
+ verify(mockInputMonitorFactory, times(2)).create(any(), any())
+ verify(mockInputMonitor, times(1)).dispose()
+ }
+
+ @Test
+ fun testCaptionIsNotCreatedWhenKeyguardIsVisible() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>()
+ verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture())
+
+ keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged(
+ true /* visible */,
+ true /* occluded */,
+ false /* animatingDismiss */
+ )
+ onTaskOpening(task)
+
+ task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
+ task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED)
+ onTaskChanging(task)
+
+ verify(mockDesktopModeWindowDecorFactory, never())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun testRelayoutBlockedDuringRecentsTransition() {
+ val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>()
+ verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture())
+
+ val transition = mock(IBinder::class.java)
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration = setUpMockDecorationForTask(task)
+
+ // Make sure a window decorations exists first by launching a freeform task.
+ onTaskOpening(task)
+ // Now call back when a Recents transition starts.
+ recentsCaptor.firstValue.onTransitionStarted(transition)
+
+ verify(decoration).incrementRelayoutBlock()
+ verify(decoration).addTransitionPausingRelayout(transition)
+ }
+
+ private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskOpening(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ desktopModeWindowDecorViewModel.onTaskChanging(
+ task,
+ leash,
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction()
+ )
+ }
+
+ private fun createTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ @WindowConfiguration.WindowingMode windowingMode: Int,
+ activityType: Int = ACTIVITY_TYPE_STANDARD,
+ focused: Boolean = true
+ ): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setWindowingMode(windowingMode)
+ .setVisible(true)
+ .setActivityType(activityType)
+ .build().apply {
+ isFocused = focused
+ }
+ }
+
+ private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration {
+ val decoration = mock(DesktopModeWindowDecoration::class.java)
+ whenever(mockDesktopModeWindowDecorFactory.create(
+ any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ ).thenReturn(decoration)
+ return decoration
+ }
+
+ private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) {
+ tasks.forEach { setUpMockDecorationForTask(it) }
+ }
+
+ private fun createVirtualDisplay(): VirtualDisplay? {
+ val surfaceView = SurfaceView(mContext)
+ return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay(
+ "testEventReceiversOnMultipleDisplays",
+ /*width=*/ 400,
+ /*height=*/ 400,
+ /*densityDpi=*/320,
+ surfaceView.holder.surface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ )
+ }
+
+ private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) {
+ configuration.windowConfiguration.windowingMode = mode
+ }
+
+ private fun RunningTaskInfo.setActivityType(type: Int) {
+ configuration.windowConfiguration.activityType = type
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeWindowDecorViewModelTests"
+ private val STABLE_INSETS = Rect(0, 100, 0, 0)
+ }
+}
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
new file mode 100644
index 000000000000..a2dbab197fb5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+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.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.function.Supplier;
+
+/**
+ * Tests for {@link DesktopModeWindowDecoration}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DesktopModeWindowDecorationTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DesktopModeWindowDecorationTests extends ShellTestCase {
+ @Mock
+ private DisplayController mMockDisplayController;
+ @Mock
+ private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock
+ private Handler mMockHandler;
+ @Mock
+ private Choreographer mMockChoreographer;
+ @Mock
+ private SyncTransactionQueue mMockSyncQueue;
+ @Mock
+ private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
+ @Mock
+ private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
+ @Mock
+ private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private SurfaceControl mMockSurfaceControl;
+ @Mock
+ private SurfaceControlViewHost mMockSurfaceControlViewHost;
+ @Mock
+ private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setUp() {
+ doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
+ any(), any(), any());
+ doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+ }
+
+ @Test
+ public void testMenusClosedWhenTaskIsInvisible() {
+ doReturn(mMockTransaction).when(mMockTransaction).hide(any());
+
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(false /* visible */);
+ final DesktopModeWindowDecoration spyWindowDecor =
+ spy(createWindowDecoration(taskInfo));
+
+ spyWindowDecor.relayout(taskInfo);
+
+ // Menus should close if open before the task being invisible causes relayout to return.
+ verify(spyWindowDecor).closeHandleMenu();
+ verify(spyWindowDecor).closeMaximizeMenu();
+
+ }
+
+ private DesktopModeWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo) {
+ return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
+ mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration,
+ mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+ SurfaceControl.Builder::new, mMockTransactionSupplier,
+ WindowContainerTransaction::new, mMockSurfaceControlViewHostFactory);
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(visible)
+ .build();
+ taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
+ "DesktopModeWindowDecorationTests");
+ return taskInfo;
+
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index de46b31879ed..5c0e04aecf6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -76,7 +76,7 @@ class DragPositioningCallbackUtilityTest {
minHeight = MIN_HEIGHT
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
- configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 6f0599aa8243..c0c4498e3ebf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -88,7 +88,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
minHeight = MIN_HEIGHT
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
- configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 3465ddd9d101..8913453aa578 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -104,7 +104,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
minHeight = MIN_HEIGHT
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
- configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
}
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
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 7fc1c99bb44e..76bc25aa66ef 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
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
@@ -116,6 +117,7 @@ public class WindowDecorationTests extends ShellTestCase {
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
+ private Configuration mWindowConfiguration = new Configuration();
private int mCaptionMenuWidthId;
private int mCaptionMenuShadowRadiusId;
private int mCaptionMenuCornerRadiusId;
@@ -296,6 +298,7 @@ public class WindowDecorationTests extends ShellTestCase {
taskInfo.isFocused = true;
// Density is 2. Shadow radius is 10px. Caption height is 64px.
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mWindowConfiguration.densityDpi = taskInfo.configuration.densityDpi;
final SurfaceControl taskSurface = mock(SurfaceControl.class);
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
@@ -516,7 +519,7 @@ public class WindowDecorationTests extends ShellTestCase {
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
- taskInfo, testSurface,
+ taskInfo, testSurface, mWindowConfiguration,
new MockObjectSupplier<>(mMockSurfaceControlBuilders,
() -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
new MockObjectSupplier<>(mMockSurfaceControlTransactions,
@@ -556,13 +559,15 @@ public class WindowDecorationTests extends ShellTestCase {
TestWindowDecoration(Context context, DisplayController displayController,
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
+ Configuration windowConfiguration,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
- windowContainerTransactionSupplier, surfaceControlViewHostFactory);
+ windowConfiguration, surfaceControlBuilderSupplier,
+ surfaceControlTransactionSupplier, windowContainerTransactionSupplier,
+ surfaceControlViewHostFactory);
}
@Override
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 4fb80ac13bbf..47a7f3579764 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -257,6 +257,7 @@ cc_benchmark {
"tests/AssetManager2_bench.cpp",
"tests/AttributeResolution_bench.cpp",
"tests/CursorWindow_bench.cpp",
+ "tests/Generic_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
index 32d2c5b05acb..7eedfdb5c921 100644
--- a/libs/androidfw/ApkParsing.cpp
+++ b/libs/androidfw/ApkParsing.cpp
@@ -56,6 +56,11 @@ const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit,
return nullptr;
}
+ // Make sure file starts with 'lib/' prefix.
+ if (strncmp(fileName, APK_LIB.data(), APK_LIB_LEN) != 0) {
+ return nullptr;
+ }
+
// Make sure there aren't subdirectories by checking if the next / after lib/ is the last slash
if (memchr(fileName + APK_LIB_LEN, '/', fileNameLen - APK_LIB_LEN) != lastSlash) {
return nullptr;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 5e04bfea2bef..7f226939ffaa 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -97,12 +97,17 @@ struct Theme::Entry {
Res_value value;
};
-AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration)
- : configuration_(configuration) {
+AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) {
+ configurations_.push_back(configuration);
+
// Don't invalidate caches here as there's nothing cached yet.
SetApkAssets(apk_assets, false);
}
+AssetManager2::AssetManager2() {
+ configurations_.resize(1);
+}
+
bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
BuildDynamicRefTable(apk_assets);
RebuildFilterList();
@@ -266,7 +271,7 @@ void AssetManager2::DumpToLog() const {
auto op = StartOperation();
std::string list;
- for (size_t i = 0; i < apk_assets_.size(); ++i) {
+ for (size_t i = 0, s = apk_assets_.size(); i < s; ++i) {
const auto& assets = GetApkAssets(i);
base::StringAppendF(&list, "%s,", assets ? assets->GetDebugName().c_str() : "nullptr");
}
@@ -359,7 +364,7 @@ bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name,
std::string* out) const {
auto op = StartOperation();
uint8_t package_id = 0U;
- for (size_t i = 0; i != apk_assets_.size(); ++i) {
+ for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) {
const auto& assets = GetApkAssets(i);
if (!assets) {
continue;
@@ -418,7 +423,7 @@ bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name,
bool AssetManager2::ContainsAllocatedTable() const {
auto op = StartOperation();
- for (size_t i = 0; i != apk_assets_.size(); ++i) {
+ for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) {
const auto& assets = GetApkAssets(i);
if (assets && assets->IsTableAllocated()) {
return true;
@@ -427,9 +432,16 @@ bool AssetManager2::ContainsAllocatedTable() const {
return false;
}
-void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
- const int diff = configuration_.diff(configuration);
- configuration_ = configuration;
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+ int diff = 0;
+ if (configurations_.size() != configurations.size()) {
+ diff = -1;
+ } else {
+ for (int i = 0; i < configurations_.size(); i++) {
+ diff |= configurations_[i].diff(configurations[i]);
+ }
+ }
+ configurations_ = std::move(configurations);
if (diff) {
RebuildFilterList();
@@ -626,16 +638,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
auto op = StartOperation();
- // Might use this if density_override != 0.
- ResTable_config density_override_config;
-
- // Select our configuration or generate a density override configuration.
- const ResTable_config* desired_config = &configuration_;
- if (density_override != 0 && density_override != configuration_.density) {
- density_override_config = configuration_;
- density_override_config.density = density_override;
- desired_config = &density_override_config;
- }
// Retrieve the package group from the package id of the resource id.
if (UNLIKELY(!is_valid_resid(resid))) {
@@ -654,119 +656,160 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
}
const PackageGroup& package_group = package_groups_[package_idx];
- auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- stop_at_first_match, ignore_configuration);
- if (UNLIKELY(!result.has_value())) {
- return base::unexpected(result.error());
- }
+ std::optional<FindEntryResult> final_result;
+ bool final_has_locale = false;
+ bool final_overlaid = false;
+ for (auto & config : configurations_) {
+ // Might use this if density_override != 0.
+ ResTable_config density_override_config;
+
+ // Select our configuration or generate a density override configuration.
+ const ResTable_config* desired_config = &config;
+ if (density_override != 0 && density_override != config.density) {
+ density_override_config = config;
+ density_override_config.density = density_override;
+ desired_config = &density_override_config;
+ }
- bool overlaid = false;
- if (!stop_at_first_match && !ignore_configuration) {
- const auto& assets = GetApkAssets(result->cookie);
- if (!assets) {
- ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
- return base::unexpected(std::nullopt);
+ auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ stop_at_first_match, ignore_configuration);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
}
- if (!assets->IsLoader()) {
- for (const auto& id_map : package_group.overlays_) {
- auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
- if (!overlay_entry) {
- // No id map entry exists for this target resource.
- continue;
- }
- if (overlay_entry.IsInlineValue()) {
- // The target resource is overlaid by an inline value not represented by a resource.
- ConfigDescription best_frro_config;
- Res_value best_frro_value;
- bool frro_found = false;
- for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
- if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
- && config.match(*desired_config)) {
- frro_found = true;
- best_frro_config = config;
- best_frro_value = value;
+ bool overlaid = false;
+ if (!stop_at_first_match && !ignore_configuration) {
+ const auto& assets = GetApkAssets(result->cookie);
+ if (!assets) {
+ ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
+ return base::unexpected(std::nullopt);
+ }
+ if (!assets->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
+ continue;
+ }
+ if (overlay_entry.IsInlineValue()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ ConfigDescription best_frro_config;
+ Res_value best_frro_value;
+ bool frro_found = false;
+ for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
+ if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
+ && config.match(*desired_config)) {
+ frro_found = true;
+ best_frro_config = config;
+ best_frro_value = value;
+ }
}
+ if (!frro_found) {
+ continue;
+ }
+ result->entry = best_frro_value;
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ result->cookie = id_map.cookie;
+
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.steps.push_back(Resolution::Step{
+ Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
+ if (auto path = assets->GetPath()) {
+ const std::string overlay_path = path->data();
+ if (IsFabricatedOverlay(overlay_path)) {
+ // FRRO don't have package name so we use the creating package here.
+ String8 frro_name = String8("FRRO");
+ // Get the first part of it since the expected one should be like
+ // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
+ // under /data/resource-cache/.
+ const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
+ const size_t end = name.find('-');
+ if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
+ frro_name.append(base::StringPrintf(" created by %s",
+ name.substr(0 /* pos */,
+ end).c_str()).c_str());
+ }
+ last_resolution_.best_package_name = frro_name;
+ } else {
+ last_resolution_.best_package_name = result->package_name->c_str();
+ }
+ }
+ overlaid = true;
+ }
+ continue;
}
- if (!frro_found) {
+
+ auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(overlay_result))) {
+ return base::unexpected(overlay_result.error());
+ }
+ if (!overlay_result.has_value()) {
+ continue;
+ }
+
+ if (!overlay_result->config.isBetterThan(result->config, desired_config)
+ && overlay_result->config.compare(result->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the
+ // target configuration to be chosen as the better value.
continue;
}
- result->entry = best_frro_value;
+
+ result->cookie = overlay_result->cookie;
+ result->entry = overlay_result->entry;
+ result->config = overlay_result->config;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- result->cookie = id_map.cookie;
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie});
- if (auto path = assets->GetPath()) {
- const std::string overlay_path = path->data();
- if (IsFabricatedOverlay(overlay_path)) {
- // FRRO don't have package name so we use the creating package here.
- String8 frro_name = String8("FRRO");
- // Get the first part of it since the expected one should be like
- // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
- // under /data/resource-cache/.
- const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
- const size_t end = name.find('-');
- if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
- frro_name.append(base::StringPrintf(" created by %s",
- name.substr(0 /* pos */,
- end).c_str()).c_str());
- }
- last_resolution_.best_package_name = frro_name;
- } else {
- last_resolution_.best_package_name = result->package_name->c_str();
- }
- }
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
+ overlay_result->config.toString()});
+ last_resolution_.best_package_name =
+ overlay_result->package_name->c_str();
overlaid = true;
}
- continue;
- }
-
- auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
- false /* stop_at_first_match */,
- false /* ignore_configuration */);
- if (UNLIKELY(IsIOError(overlay_result))) {
- return base::unexpected(overlay_result.error());
- }
- if (!overlay_result.has_value()) {
- continue;
}
+ }
+ }
- if (!overlay_result->config.isBetterThan(result->config, desired_config)
- && overlay_result->config.compare(result->config) != 0) {
- // The configuration of the entry for the overlay must be equal to or better than the target
- // configuration to be chosen as the better value.
- continue;
- }
-
- result->cookie = overlay_result->cookie;
- result->entry = overlay_result->entry;
- result->config = overlay_result->config;
- result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
-
- if (UNLIKELY(logging_enabled)) {
- last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
- overlay_result->cookie});
- last_resolution_.best_package_name =
- overlay_result->package_name->c_str();
- overlaid = true;
- }
+ bool has_locale = false;
+ if (result->config.locale == 0) {
+ if (default_locale_ != 0) {
+ ResTable_config conf;
+ conf.locale = default_locale_;
+ // Since we know conf has a locale and only a locale, match will tell us if that locale
+ // matches
+ has_locale = conf.match(config);
}
+ } else {
+ has_locale = true;
+ }
+
+ // if we don't have a result yet
+ if (!final_result ||
+ // or this config is better before the locale than the existing result
+ result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ // or the existing config isn't better before locale and this one specifies a locale
+ // whereas the existing one doesn't
+ (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ && has_locale && !final_has_locale)) {
+ final_result = result.value();
+ final_overlaid = overlaid;
+ final_has_locale = has_locale;
}
}
if (UNLIKELY(logging_enabled)) {
- last_resolution_.cookie = result->cookie;
- last_resolution_.type_string_ref = result->type_string_ref;
- last_resolution_.entry_string_ref = result->entry_string_ref;
- last_resolution_.best_config_name = result->config.toString();
- if (!overlaid) {
- last_resolution_.best_package_name = result->package_name->c_str();
+ last_resolution_.cookie = final_result->cookie;
+ last_resolution_.type_string_ref = final_result->type_string_ref;
+ last_resolution_.entry_string_ref = final_result->entry_string_ref;
+ last_resolution_.best_config_name = final_result->config.toString();
+ if (!final_overlaid) {
+ last_resolution_.best_package_name = final_result->package_name->c_str();
}
}
- return result;
+ return *final_result;
}
base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
@@ -784,8 +827,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
// If `desired_config` is not the same as the set configuration or the caller will accept a value
// from any configuration, then we cannot use our filtered list of types since it only it contains
// types matched to the set configuration.
- const bool use_filtered = !ignore_configuration && &desired_config == &configuration_;
-
+ const bool use_filtered = !ignore_configuration && std::find_if(
+ configurations_.begin(), configurations_.end(),
+ [&desired_config](auto& value) { return &desired_config == &value; })
+ != configurations_.end();
const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -834,8 +879,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
} else {
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
- this_config.toString(),
- cookie});
+ cookie, this_config.toString()});
}
continue;
}
@@ -851,8 +895,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
if (!offset.has_value()) {
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
- this_config.toString(),
- cookie});
+ cookie, this_config.toString()});
}
continue;
}
@@ -865,8 +908,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- cookie});
+ cookie, this_config.toString()});
}
// Any configuration will suffice, so break.
@@ -895,11 +937,11 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
.entry = *entry,
.config = *best_config,
.type_flags = type_flags,
+ .dynamic_ref_table = package_group.dynamic_ref_table.get(),
.package_name = &best_package->GetPackageName(),
.type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
.entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
(*best_entry_verified)->key()),
- .dynamic_ref_table = package_group.dynamic_ref_table.get(),
};
}
@@ -943,27 +985,40 @@ std::string AssetManager2::GetLastResourceResolution() const {
}
std::stringstream log_stream;
- log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
- "\tFor config - %s", resid, resource_name_string.c_str(),
- configuration_.toString().c_str());
-
+ if (configurations_.size() == 1) {
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
+ "\tFor config - %s", resid, resource_name_string.c_str(),
+ configurations_[0].toString().c_str());
+ } else {
+ ResTable_config conf = configurations_[0];
+ conf.clearLocale();
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales",
+ resid, resource_name_string.c_str(), conf.toString().c_str());
+ char str[40];
+ str[0] = '\0';
+ for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) {
+ iter->getBcp47Locale(str);
+ log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : "");
+ }
+ }
for (const Resolution::Step& step : last_resolution_.steps) {
- const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = {
- {Resolution::Step::Type::INITIAL, "Found initial"},
- {Resolution::Step::Type::BETTER_MATCH, "Found better"},
- {Resolution::Step::Type::OVERLAID, "Overlaid"},
- {Resolution::Step::Type::OVERLAID_INLINE, "Overlaid inline"},
- {Resolution::Step::Type::SKIPPED, "Skipped"},
- {Resolution::Step::Type::NO_ENTRY, "No entry"}
+ constexpr static std::array kStepStrings = {
+ "Found initial",
+ "Found better",
+ "Overlaid",
+ "Overlaid inline",
+ "Skipped",
+ "No entry"
};
- const auto prefix = kStepStrings.find(step.type);
- if (prefix == kStepStrings.end()) {
+ if (step.type < Resolution::Step::Type::INITIAL
+ || step.type > Resolution::Step::Type::NO_ENTRY) {
continue;
}
+ const auto prefix = kStepStrings[int(step.type) - int(Resolution::Step::Type::INITIAL)];
const auto& assets = GetApkAssets(step.cookie);
- log_stream << "\n\t" << prefix->second << ": "
- << (assets ? assets->GetDebugName() : "<null>") << " #" << step.cookie;
+ log_stream << "\n\t" << prefix << ": " << (assets ? assets->GetDebugName() : "<null>")
+ << " #" << step.cookie;
if (!step.config_name.empty()) {
log_stream << " - " << step.config_name;
}
@@ -1100,16 +1155,19 @@ base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
}
}
-const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const {
- auto cached_iter = cached_bag_resid_stacks_.find(resid);
- if (cached_iter != cached_bag_resid_stacks_.end()) {
- return cached_iter->second;
+base::expected<const std::vector<uint32_t>*, NullOrIOError> AssetManager2::GetBagResIdStack(
+ uint32_t resid) const {
+ auto it = cached_bag_resid_stacks_.find(resid);
+ if (it != cached_bag_resid_stacks_.end()) {
+ return &it->second;
+ }
+ std::vector<uint32_t> stacks;
+ if (auto maybe_bag = GetBag(resid, stacks); UNLIKELY(IsIOError(maybe_bag))) {
+ return base::unexpected(maybe_bag.error());
}
- std::vector<uint32_t> found_resids;
- GetBag(resid, found_resids);
- cached_bag_resid_stacks_.emplace(resid, found_resids);
- return found_resids;
+ it = cached_bag_resid_stacks_.emplace(resid, std::move(stacks)).first;
+ return &it->second;
}
base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
@@ -1126,9 +1184,15 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
}
base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
- std::vector<uint32_t> found_resids;
- const auto bag = GetBag(resid, found_resids);
- cached_bag_resid_stacks_.emplace(resid, std::move(found_resids));
+ auto resid_stacks_it = cached_bag_resid_stacks_.find(resid);
+ if (resid_stacks_it == cached_bag_resid_stacks_.end()) {
+ resid_stacks_it = cached_bag_resid_stacks_.emplace(resid, std::vector<uint32_t>{}).first;
+ }
+ const auto bag = GetBag(resid, resid_stacks_it->second);
+ if (UNLIKELY(IsIOError(bag))) {
+ cached_bag_resid_stacks_.erase(resid_stacks_it);
+ return base::unexpected(bag.error());
+ }
return bag;
}
@@ -1426,11 +1490,14 @@ void AssetManager2::RebuildFilterList() {
package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
- if (type_entry.config.match(configuration_)) {
- if (!group) {
- group = &package.filtered_configs_.editItemAt(type_id - 1);
+ for (auto & config : configurations_) {
+ if (type_entry.config.match(config)) {
+ if (!group) {
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
+ }
+ group->type_entries.push_back(&type_entry);
+ break;
}
- group->type_entries.push_back(&type_entry);
}
}
});
@@ -1441,25 +1508,40 @@ void AssetManager2::RebuildFilterList() {
}
void AssetManager2::InvalidateCaches(uint32_t diff) {
- cached_bag_resid_stacks_.clear();
+ cached_resolved_values_.clear();
if (diff == 0xffffffffu) {
// Everything must go.
cached_bags_.clear();
+ cached_bag_resid_stacks_.clear();
return;
}
// Be more conservative with what gets purged. Only if the bag has other possible
// variations with respect to what changed (diff) should we remove it.
- for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
- if (diff & iter->second->type_spec_flags) {
- iter = cached_bags_.erase(iter);
+ for (auto stack_it = cached_bag_resid_stacks_.begin();
+ stack_it != cached_bag_resid_stacks_.end();) {
+ const auto it = cached_bags_.find(stack_it->first);
+ if (it == cached_bags_.end()) {
+ stack_it = cached_bag_resid_stacks_.erase(stack_it);
+ } else if ((diff & it->second->type_spec_flags) != 0) {
+ cached_bags_.erase(it);
+ stack_it = cached_bag_resid_stacks_.erase(stack_it);
} else {
- ++iter;
+ ++stack_it; // Keep the item in both caches.
}
}
- cached_resolved_values_.clear();
+ // Need to ensure that both bag caches are consistent, as we populate them in the same function.
+ // Iterate over the cached bags to erase the items without the corresponding resid_stack cache
+ // items.
+ for (auto it = cached_bags_.begin(); it != cached_bags_.end();) {
+ if ((diff & it->second->type_spec_flags) != 0) {
+ it = cached_bags_.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 89835742c8ff..5f98b8f8db43 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -294,14 +294,14 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
dtohl(header->version), kIdmapCurrentVersion);
return {};
}
+ std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
+ if (!target_path) {
+ return {};
+ }
std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
if (!overlay_path) {
return {};
}
- std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
- if (!target_path) {
- return {};
- }
if (!ReadString(&data_ptr, &data_size, "target name") ||
!ReadString(&data_ptr, &data_size, "debug info")) {
return {};
@@ -364,7 +364,7 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
return std::unique_ptr<LoadedIdmap>(
new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries,
target_inline_entries, target_inline_entry_values, configurations,
- overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
+ overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
}
bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index fbfae5e2bcbe..c9d5e074271b 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -494,6 +494,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
&loaded_package->package_name_);
+ const bool only_overlayable = (property_flags & PROPERTY_ONLY_OVERLAYABLES) != 0;
+
// A map of TypeSpec builders, each associated with an type index.
// We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
// contiguous block of memory that holds all the Types together with the TypeSpec.
@@ -502,6 +504,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
while (iter.HasNext()) {
const Chunk child_chunk = iter.Next();
+ if (only_overlayable && child_chunk.type() != RES_TABLE_OVERLAYABLE_TYPE) {
+ continue;
+ }
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE: {
const auto pool_address = child_chunk.header<ResChunk_header>();
@@ -655,6 +660,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
<< name_to_actor_it->first << "'.";
return {};
}
+ if (only_overlayable) {
+ break;
+ }
// Iterate over the overlayable policy chunks contained within the overlayable chunk data
ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
@@ -800,14 +808,21 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
}
+ const bool only_overlayable = (property_flags & PROPERTY_ONLY_OVERLAYABLES) != 0;
+
const size_t package_count = dtohl(header->packageCount);
size_t packages_seen = 0;
- packages_.reserve(package_count);
+ if (!only_overlayable) {
+ packages_.reserve(package_count);
+ }
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
while (iter.HasNext()) {
const Chunk child_chunk = iter.Next();
+ if (only_overlayable && child_chunk.type() != RES_TABLE_PACKAGE_TYPE) {
+ continue;
+ }
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE:
// Only use the first string pool. Ignore others.
@@ -837,6 +852,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
return false;
}
packages_.push_back(std::move(loaded_package));
+ if (only_overlayable) {
+ // Overlayable is always in the first package, no need to process anything else.
+ return true;
+ }
} break;
default:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index c13827fe29c3..4c992becda7c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1769,13 +1769,21 @@ ResXMLTree::~ResXMLTree()
status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
{
+ const ResChunk_header* chunk = nullptr;
+ const ResChunk_header* lastChunk = nullptr;
+
uninit();
mEventCode = START_DOCUMENT;
if (!data || !size) {
return (mError=BAD_TYPE);
}
-
+ if (size < sizeof(ResXMLTree_header)) {
+ ALOGW("Bad XML block: total size %d is less than the header size %d\n",
+ int(size), int(sizeof(ResXMLTree_header)));
+ mError = BAD_TYPE;
+ goto done;
+ }
if (copyData) {
mOwnedData = malloc(size);
if (mOwnedData == NULL) {
@@ -1792,9 +1800,15 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
(int)dtohs(mHeader->header.headerSize),
(int)dtohl(mHeader->header.size), (int)size);
mError = BAD_TYPE;
- restart();
- return mError;
+ goto done;
}
+ if (dtohs(mHeader->header.type) != RES_XML_TYPE) {
+ ALOGW("Bad XML block: expected root block type %d, got %d\n",
+ int(RES_XML_TYPE), int(dtohs(mHeader->header.type)));
+ mError = BAD_TYPE;
+ goto done;
+ }
+
mDataEnd = ((const uint8_t*)mHeader) + mSize;
mStrings.uninit();
@@ -1804,9 +1818,8 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
// First look for a couple interesting chunks: the string block
// and first XML node.
- const ResChunk_header* chunk =
- (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize));
- const ResChunk_header* lastChunk = chunk;
+ chunk = (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize));
+ lastChunk = chunk;
while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) &&
((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) {
status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML");
@@ -1860,7 +1873,11 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
mError = mStrings.getError();
done:
- restart();
+ if (mError) {
+ uninit();
+ } else {
+ restart();
+ }
return mError;
}
@@ -2551,6 +2568,22 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
return false;
}
+bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
+ const ResTable_config* requested) const {
+ if (requested) {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested->mcc) {
+ return (mcc);
+ }
+
+ if ((mnc != o.mnc) && requested->mnc) {
+ return (mnc);
+ }
+ }
+ }
+ return false;
+}
+
bool ResTable_config::isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
@@ -5436,37 +5469,66 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
return U16StringToInt(s, len, outValue);
}
-bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
-{
- while (len > 0 && isspace16(*s)) {
- s++;
- len--;
+template <typename T>
+bool parseFloatingPoint(const char16_t* inBuf, size_t inLen, char* tempBuf,
+ const char** outEnd, T& out){
+ while (inLen > 0 && isspace16(*inBuf)) {
+ inBuf++;
+ inLen--;
}
- if (len <= 0) {
+ if (inLen <= 0) {
return false;
}
- char buf[128];
int i=0;
- while (len > 0 && *s != 0 && i < 126) {
- if (*s > 255) {
+ while (inLen > 0 && *inBuf != 0 && i < 126) {
+ if (*inBuf > 255) {
return false;
}
- buf[i++] = *s++;
- len--;
+ tempBuf[i++] = *inBuf++;
+ inLen--;
}
- if (len > 0) {
+ if (inLen > 0) {
+ return false;
+ }
+ if ((tempBuf[0] < '0' || tempBuf[0] > '9') && tempBuf[0] != '.' && tempBuf[0] != '-' && tempBuf[0] != '+') {
return false;
}
- if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
+
+ tempBuf[i] = 0;
+ if constexpr(std::is_same_v<T, float>) {
+ out = strtof(tempBuf, (char**)outEnd);
+ } else {
+ out = strtod(tempBuf, (char**)outEnd);
+ }
+ return true;
+}
+
+bool ResTable::stringToDouble(const char16_t* s, size_t len, double& d){
+ char buf[128];
+ const char* end = nullptr;
+ if (!parseFloatingPoint(s, len, buf, &end, d)) {
return false;
}
- buf[i] = 0;
- const char* end;
- float f = strtof(buf, (char**)&end);
+ while (*end != 0 && isspace((unsigned char)*end)) {
+ end++;
+ }
+
+ return *end == 0;
+}
+
+bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
+{
+ char buf[128];
+ const char* end = nullptr;
+ float f;
+
+ if (!parseFloatingPoint(s, len, buf, &end, f)) {
+ return false;
+ }
if (*end != 0 && !isspace((unsigned char)*end)) {
// Might be a unit...
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 52e7a70521a1..d7b5914130ee 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -40,17 +40,24 @@ class _ZipEntryRO {
public:
ZipEntry entry;
std::string_view name;
- void *cookie;
+ void *cookie = nullptr;
- _ZipEntryRO() : cookie(NULL) {}
+ _ZipEntryRO() = default;
~_ZipEntryRO() {
- EndIteration(cookie);
+ EndIteration(cookie);
+ }
+
+ android::ZipEntryRO convertToPtr() {
+ _ZipEntryRO* result = new _ZipEntryRO;
+ result->entry = std::move(this->entry);
+ result->name = std::move(this->name);
+ result->cookie = std::exchange(this->cookie, nullptr);
+ return result;
}
private:
- _ZipEntryRO(const _ZipEntryRO& other);
- _ZipEntryRO& operator=(const _ZipEntryRO& other);
+ DISALLOW_COPY_AND_ASSIGN(_ZipEntryRO);
};
ZipFileRO::~ZipFileRO() {
@@ -94,17 +101,15 @@ ZipFileRO::~ZipFileRO() {
ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
{
- _ZipEntryRO* data = new _ZipEntryRO;
-
- data->name = entryName;
+ _ZipEntryRO data;
+ data.name = entryName;
- const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
+ const int32_t error = FindEntry(mHandle, entryName, &(data.entry));
if (error) {
- delete data;
- return NULL;
+ return nullptr;
}
- return (ZipEntryRO) data;
+ return data.convertToPtr();
}
/*
@@ -143,35 +148,50 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
}
bool ZipFileRO::startIteration(void** cookie) {
- return startIteration(cookie, NULL, NULL);
+ return startIteration(cookie, nullptr, nullptr);
}
-bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix)
-{
- _ZipEntryRO* ze = new _ZipEntryRO;
- int32_t error = StartIteration(mHandle, &(ze->cookie),
+bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix) {
+ auto result = startIterationOrError(prefix, suffix);
+ if (!result.ok()) {
+ return false;
+ }
+ *cookie = result.value();
+ return true;
+}
+
+base::expected<void*, int32_t>
+ZipFileRO::startIterationOrError(const char* prefix, const char* suffix) {
+ _ZipEntryRO ze;
+ int32_t error = StartIteration(mHandle, &(ze.cookie),
prefix ? prefix : "", suffix ? suffix : "");
if (error) {
ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
ErrorCodeString(error));
- delete ze;
- return false;
+ return base::unexpected(error);
}
- *cookie = ze;
- return true;
+ return ze.convertToPtr();
}
-ZipEntryRO ZipFileRO::nextEntry(void* cookie)
-{
+ZipEntryRO ZipFileRO::nextEntry(void* cookie) {
+ auto result = nextEntryOrError(cookie);
+ if (!result.ok()) {
+ return nullptr;
+ }
+ return result.value();
+}
+
+base::expected<ZipEntryRO, int32_t> ZipFileRO::nextEntryOrError(void* cookie) {
_ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie);
int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
if (error) {
if (error != -1) {
ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
ErrorCodeString(error));
+ return base::unexpected(error);
}
- return NULL;
+ return nullptr;
}
return &(ze->entry);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index fc391bc2ce67..d9ff35b49e0a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -100,7 +100,7 @@ class AssetManager2 {
using ApkAssetsWPtr = wp<const ApkAssets>;
using ApkAssetsList = std::span<const ApkAssetsPtr>;
- AssetManager2() = default;
+ AssetManager2();
explicit AssetManager2(AssetManager2&& other) = default;
AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration);
@@ -156,10 +156,14 @@ class AssetManager2 {
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
- void SetConfiguration(const ResTable_config& configuration);
+ void SetConfigurations(std::vector<ResTable_config> configurations);
- inline const ResTable_config& GetConfiguration() const {
- return configuration_;
+ inline const std::vector<ResTable_config>& GetConfigurations() const {
+ return configurations_;
+ }
+
+ inline void SetDefaultLocale(uint32_t default_locale) {
+ default_locale_ = default_locale;
}
// Returns all configurations for which there are resources defined, or an I/O error if reading
@@ -243,9 +247,14 @@ class AssetManager2 {
friend AssetManager2;
friend Theme;
SelectedValue() = default;
- SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) :
- cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType),
- flags(bag->type_spec_flags), resid(0U), config({}) {};
+ SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry)
+ : cookie(entry.cookie),
+ data(entry.value.data),
+ type(entry.value.dataType),
+ flags(bag->type_spec_flags),
+ resid(0U),
+ config() {
+ }
// The cookie representing the ApkAssets in which the value resides.
ApkAssetsCookie cookie = kInvalidCookie;
@@ -327,7 +336,8 @@ class AssetManager2 {
// resource data failed.
base::expected<uint32_t, NullOrIOError> GetResourceTypeSpecFlags(uint32_t resid) const;
- const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
+ base::expected<const std::vector<uint32_t>*, NullOrIOError> GetBagResIdStack(
+ uint32_t resid) const;
// Resets the resource resolution structures in preparation for the next resource retrieval.
void ResetResourceResolution() const;
@@ -459,9 +469,11 @@ class AssetManager2 {
// without taking too much memory.
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
- // The current configuration set for this AssetManager. When this changes, cached resources
+ uint32_t default_locale_;
+
+ // The current configurations set for this AssetManager. When this changes, cached resources
// may need to be purged.
- ResTable_config configuration_ = {};
+ std::vector<ResTable_config> configurations_;
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
@@ -495,10 +507,10 @@ class AssetManager2 {
// Marks what kind of override this step was.
Type type;
+ ApkAssetsCookie cookie = kInvalidCookie;
+
// Built name of configuration for this step.
String8 config_name;
-
- ApkAssetsCookie cookie = kInvalidCookie;
};
// Last resolved resource ID.
diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h
index 948162d10480..6667747fc581 100644
--- a/libs/androidfw/include/androidfw/Errors.h
+++ b/libs/androidfw/include/androidfw/Errors.h
@@ -34,7 +34,7 @@ using NullOrIOError = std::variant<std::nullopt_t, IOError>;
// Checks whether the result holds an unexpected I/O error.
template <typename T>
-static inline bool IsIOError(const base::expected<T, NullOrIOError> result) {
+static inline bool IsIOError(const base::expected<T, NullOrIOError>& result) {
return !result.has_value() && std::holds_alternative<IOError>(result.error());
}
diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h
index 4d5844eaa069..865a298f8389 100644
--- a/libs/androidfw/include/androidfw/IDiagnostics.h
+++ b/libs/androidfw/include/androidfw/IDiagnostics.h
@@ -86,6 +86,17 @@ struct IDiagnostics {
DiagMessageActual actual = message.Build();
Log(Level::Note, actual);
}
+
+ virtual void SetVerbose(bool val) {
+ verbose_ = val;
+ }
+
+ virtual bool IsVerbose() {
+ return verbose_;
+ }
+
+ private:
+ bool verbose_ = false;
};
class SourcePathDiagnostics : public IDiagnostics {
@@ -105,6 +116,14 @@ class SourcePathDiagnostics : public IDiagnostics {
return error;
}
+ void SetVerbose(bool val) override {
+ diag_->SetVerbose(val);
+ }
+
+ bool IsVerbose() override {
+ return diag_->IsVerbose();
+ }
+
private:
Source source_;
IDiagnostics* diag_;
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 4d12885ad291..3a7287187781 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -96,6 +96,9 @@ enum : package_property_t {
// The apk assets is owned by the application running in this process and incremental crash
// protections for this APK must be disabled.
PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U,
+
+ // The apk assets only contain the overlayable declarations information.
+ PROPERTY_ONLY_OVERLAYABLES = 1U << 5U,
};
struct OverlayableInfo {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 631bda4f886c..fdb355192676 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1375,6 +1375,8 @@ struct ResTable_config
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+
String8 toString() const;
};
@@ -1870,6 +1872,8 @@ struct FabricatedOverlayEntryParameters {
DataValue data_value;
std::string data_string_value;
std::optional<android::base::borrowed_fd> data_binary_value;
+ off64_t binary_data_offset;
+ size_t binary_data_size;
std::string configuration;
};
@@ -2162,6 +2166,7 @@ public:
static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue);
static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue);
+ static bool stringToDouble(const char16_t* s, size_t len, double& outValue);
// Used with stringToValue.
class Accessor
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 10f6d0655bf4..be1f98f4843d 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -37,6 +37,8 @@
#include <unistd.h>
#include <time.h>
+#include <android-base/expected.h>
+
#include <util/map_ptr.h>
#include <utils/Compat.h>
@@ -102,6 +104,11 @@ public:
*/
bool startIteration(void** cookie);
bool startIteration(void** cookie, const char* prefix, const char* suffix);
+ /*
+ * Same as above, but returns the error code in case of failure.
+ * #see libziparchive/zip_error.h.
+ */
+ base::expected<void*, int32_t> startIterationOrError(const char* prefix, const char* suffix);
/**
* Return the next entry in iteration order, or NULL if there are no more
@@ -109,6 +116,12 @@ public:
*/
ZipEntryRO nextEntry(void* cookie);
+ /**
+ * Same as above, but returns the error code in case of failure.
+ * #see libziparchive/zip_error.h.
+ */
+ base::expected<ZipEntryRO, int32_t> nextEntryOrError(void* cookie);
+
void endIteration(void* cookie);
void releaseEntry(ZipEntryRO entry) const;
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
index 62e88c619e5c..ac1dc9b88463 100644
--- a/libs/androidfw/tests/ApkParsing_test.cpp
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -74,4 +74,10 @@ TEST(ApkParsingTest, InvalidFileAtRoot) {
auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
ASSERT_THAT(lastSlash, IsNull());
}
+
+TEST(ApkParsingTest, InvalidPrefix) {
+ const char* path = "assets/libhello.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
} \ No newline at end of file
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 6fae72a6d10e..2caa98c35971 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -228,10 +228,12 @@ static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) {
ResTable_config config;
memset(&config, 0, sizeof(config));
+ std::vector<ResTable_config> configs;
+ configs.push_back(config);
while (state.KeepRunning()) {
- config.sdkVersion = ~config.sdkVersion;
- assets.SetConfiguration(config);
+ configs[0].sdkVersion = ~configs[0].sdkVersion;
+ assets.SetConfigurations(configs);
}
}
BENCHMARK(BM_AssetManagerSetConfigurationFramework);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index df3fa02ce44c..c62f095e9dac 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -113,7 +113,7 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -137,7 +137,7 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -466,10 +466,10 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) {
TEST_F(AssetManager2Test, DensityOverride) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_});
- assetmanager.SetConfiguration({
+ assetmanager.SetConfigurations({{
.density = ResTable_config::DENSITY_XHIGH,
.sdkVersion = 21,
- });
+ }});
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
ASSERT_TRUE(value.has_value());
@@ -721,7 +721,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
assetmanager.SetResourceResolutionLoggingEnabled(false);
@@ -736,7 +736,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) {
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto result = assetmanager.GetLastResourceResolution();
@@ -751,7 +751,7 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -774,7 +774,7 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -796,7 +796,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -817,7 +817,7 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({overlayable_assets_});
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index b97dd96f8934..8b883f4ed1df 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -66,7 +66,7 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_
AssetManager2 assetmanager;
assetmanager.SetApkAssets(apk_assets);
if (config != nullptr) {
- assetmanager.SetConfiguration(*config);
+ assetmanager.SetConfigurations({*config});
}
while (state.KeepRunning()) {
diff --git a/libs/androidfw/tests/Generic_bench.cpp b/libs/androidfw/tests/Generic_bench.cpp
new file mode 100644
index 000000000000..4c978e889f83
--- /dev/null
+++ b/libs/androidfw/tests/Generic_bench.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <stdint.h>
+
+#include <map>
+#include <unordered_map>
+
+#include "benchmark/benchmark.h"
+
+namespace android {
+
+template <class Map = std::unordered_map<uint32_t, std::vector<uint32_t>>>
+static Map prepare_map() {
+ Map map;
+ std::vector<uint32_t> vec;
+ for (int i = 0; i < 1000; ++i) {
+ map.emplace(i, vec);
+ }
+ return map;
+}
+
+static void BM_hashmap_emplace_same(benchmark::State& state) {
+ auto map = prepare_map<>();
+ auto val = map.size() - 1;
+ std::vector<uint32_t> vec;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.emplace(val, vec));
+ }
+}
+BENCHMARK(BM_hashmap_emplace_same);
+static void BM_hashmap_try_emplace_same(benchmark::State& state) {
+ auto map = prepare_map();
+ auto val = map.size() - 1;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.try_emplace(val));
+ }
+}
+BENCHMARK(BM_hashmap_try_emplace_same);
+static void BM_hashmap_find(benchmark::State& state) {
+ auto map = prepare_map<>();
+ auto val = map.size() - 1;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.find(val));
+ }
+}
+BENCHMARK(BM_hashmap_find);
+
+static void BM_hashmap_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map<>();
+ std::vector<uint32_t> vec;
+ auto i = map.size();
+ for (auto&& _ : state) {
+ map.emplace(i++, vec);
+ }
+}
+BENCHMARK(BM_hashmap_emplace_diff);
+static void BM_hashmap_try_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map();
+ auto i = map.size();
+ for (auto&& _ : state) {
+ map.try_emplace(i++);
+ }
+}
+BENCHMARK(BM_hashmap_try_emplace_diff);
+static void BM_hashmap_find_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map<>();
+ std::vector<uint32_t> vec;
+ auto i = map.size();
+ for (auto&& _ : state) {
+ if (map.find(i++) == map.end()) {
+ map.emplace(i - 1, vec);
+ }
+ }
+}
+BENCHMARK(BM_hashmap_find_emplace_diff);
+
+static void BM_treemap_emplace_same(benchmark::State& state) {
+ auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>();
+ auto val = map.size() - 1;
+ std::vector<uint32_t> vec;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.emplace(val, vec));
+ }
+}
+BENCHMARK(BM_treemap_emplace_same);
+static void BM_treemap_try_emplace_same(benchmark::State& state) {
+ auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>();
+ auto val = map.size() - 1;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.try_emplace(val));
+ }
+}
+BENCHMARK(BM_treemap_try_emplace_same);
+static void BM_treemap_find(benchmark::State& state) {
+ auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>();
+ auto val = map.size() - 1;
+ for (auto&& _ : state) {
+ benchmark::DoNotOptimize(map.find(val));
+ }
+}
+BENCHMARK(BM_treemap_find);
+
+static void BM_treemap_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>();
+ std::vector<uint32_t> vec;
+ auto i = map.size();
+ for (auto&& _ : state) {
+ map.emplace(i++, vec);
+ }
+}
+BENCHMARK(BM_treemap_emplace_diff);
+static void BM_treemap_try_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map();
+ auto i = map.size();
+ for (auto&& _ : state) {
+ map.try_emplace(i++);
+ }
+}
+BENCHMARK(BM_treemap_try_emplace_diff);
+static void BM_treemap_find_emplace_diff(benchmark::State& state) {
+ auto map = prepare_map();
+ std::vector<uint32_t> vec;
+ auto i = map.size();
+ for (auto&& _ : state) {
+ if (map.find(i++) == map.end()) {
+ map.emplace(i - 1, vec);
+ }
+ }
+}
+BENCHMARK(BM_treemap_find_emplace_diff);
+
+} // namespace android \ No newline at end of file
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index e08a6a7f277d..181d1411fb91 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -260,7 +260,7 @@ TEST_F(ThemeTest, ThemeRebase) {
ResTable_config night{};
night.uiMode = ResTable_config::UI_MODE_NIGHT_YES;
night.version = 8u;
- am_night.SetConfiguration(night);
+ am_night.SetConfigurations({night});
auto theme = am.NewTheme();
{
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index c87c15669a09..e1dd145edcfa 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -28,6 +28,19 @@ license {
],
}
+aconfig_declarations {
+ name: "hwui_flags",
+ package: "com.android.graphics.hwui.flags",
+ srcs: [
+ "aconfig/hwui_flags.aconfig",
+ ],
+}
+
+cc_aconfig_library {
+ name: "hwui_flags_cc_lib",
+ aconfig_declarations: "hwui_flags",
+}
+
cc_defaults {
name: "hwui_defaults",
defaults: [
@@ -44,7 +57,7 @@ cc_defaults {
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
"-DATRACE_TAG=ATRACE_TAG_VIEW",
- "-DLOG_TAG=\"OpenGLRenderer\"",
+ "-DLOG_TAG=\"HWUI\"",
"-Wall",
"-Wthread-safety",
"-Wno-unused-parameter",
@@ -139,6 +152,7 @@ cc_defaults {
"libstatspull_lazy",
"libstatssocket_lazy",
"libtonemap",
+ "hwui_flags_cc_lib",
],
},
host: {
@@ -514,6 +528,7 @@ cc_defaults {
"canvas/CanvasOpRasterizer.cpp",
"effects/StretchEffect.cpp",
"effects/GainmapRenderer.cpp",
+ "pipeline/skia/BackdropFilterDrawable.cpp",
"pipeline/skia/HolePunch.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index b656b6ac8204..4d020c567972 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -16,6 +16,11 @@
#include "AutoBackendTextureRelease.h"
+#include <SkImage.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/GrBackendSurface.h>
+#include <include/gpu/MutableTextureState.h>
#include "renderthread/RenderThread.h"
#include "utils/Color.h"
#include "utils/PaintUtils.h"
@@ -30,15 +35,47 @@ AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context,
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
- GrBackendFormat backendFormat =
- GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
+
+ GrBackendFormat backendFormat;
+ GrBackendApi backend = context->backend();
+ if (backend == GrBackendApi::kOpenGL) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeGLBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ false);
+ } else if (backend == GrBackendApi::kVulkan) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
+ buffer,
+ desc.format,
+ false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ false);
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+ }
LOG_ALWAYS_FATAL_IF(!backendFormat.isValid(),
__FILE__ " Invalid GrBackendFormat. GrBackendApi==%" PRIu32
", AHardwareBuffer_Format==%" PRIu32 ".",
static_cast<int>(context->backend()), desc.format);
- mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
- context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx,
- createProtectedImage, backendFormat, false);
LOG_ALWAYS_FATAL_IF(!mBackendTexture.isValid(),
__FILE__ " Invalid GrBackendTexture. Width==%" PRIu32 ", height==%" PRIu32
", protected==%d",
@@ -70,7 +107,7 @@ void AutoBackendTextureRelease::unref(bool releaseImage) {
// releaseProc is invoked by SkImage, when texture is no longer in use.
// "releaseContext" contains an "AutoBackendTextureRelease*".
-static void releaseProc(SkImage::ReleaseContext releaseContext) {
+static void releaseProc(SkImages::ReleaseContext releaseContext) {
AutoBackendTextureRelease* textureRelease =
reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
textureRelease->unref(false);
@@ -83,10 +120,10 @@ void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer,
AHardwareBuffer_describe(buffer, &desc);
SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
// The following ref will be counteracted by Skia calling releaseProc, either during
- // MakeFromTexture if there is a failure, or later when SkImage is discarded. It must
- // be called before MakeFromTexture, otherwise Skia may remove HWUI's ref on failure.
+ // BorrowTextureFrom if there is a failure, or later when SkImage is discarded. It must
+ // be called before BorrowTextureFrom, otherwise Skia may remove HWUI's ref on failure.
ref();
- mImage = SkImage::MakeFromTexture(
+ mImage = SkImages::BorrowTextureFrom(
context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType,
uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this);
}
@@ -105,8 +142,8 @@ void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context)
LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
if (mBackendTexture.isValid()) {
// Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
- GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED,
- VK_QUEUE_FAMILY_FOREIGN_EXT);
+ skgpu::MutableTextureState newState(VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_QUEUE_FAMILY_FOREIGN_EXT);
// The unref for this ref happens in the releaseProc passed into setBackendTextureState. The
// releaseProc callback will be made when the work to set the new state has finished on the
diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h
new file mode 100644
index 000000000000..1a5b938d6eed
--- /dev/null
+++ b/libs/hwui/ColorFilter.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COLORFILTER_H_
+#define COLORFILTER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "GraphicsJNI.h"
+#include "SkColorFilter.h"
+#include "SkiaWrapper.h"
+
+namespace android {
+namespace uirenderer {
+
+class ColorFilter : public SkiaWrapper<SkColorFilter> {
+public:
+ static ColorFilter* fromJava(jlong handle) { return reinterpret_cast<ColorFilter*>(handle); }
+
+protected:
+ ColorFilter() = default;
+};
+
+class BlendModeColorFilter : public ColorFilter {
+public:
+ BlendModeColorFilter(SkColor color, SkBlendMode mode) : mColor(color), mMode(mode) {}
+
+private:
+ sk_sp<SkColorFilter> createInstance() override { return SkColorFilters::Blend(mColor, mMode); }
+
+private:
+ const SkColor mColor;
+ const SkBlendMode mMode;
+};
+
+class LightingFilter : public ColorFilter {
+public:
+ LightingFilter(SkColor mul, SkColor add) : mMul(mul), mAdd(add) {}
+
+ void setMul(SkColor mul) {
+ mMul = mul;
+ discardInstance();
+ }
+
+ void setAdd(SkColor add) {
+ mAdd = add;
+ discardInstance();
+ }
+
+private:
+ sk_sp<SkColorFilter> createInstance() override { return SkColorFilters::Lighting(mMul, mAdd); }
+
+private:
+ SkColor mMul;
+ SkColor mAdd;
+};
+
+class ColorMatrixColorFilter : public ColorFilter {
+public:
+ ColorMatrixColorFilter(std::vector<float>&& matrix) : mMatrix(std::move(matrix)) {}
+
+ void setMatrix(std::vector<float>&& matrix) {
+ mMatrix = std::move(matrix);
+ discardInstance();
+ }
+
+private:
+ sk_sp<SkColorFilter> createInstance() override {
+ return SkColorFilters::Matrix(mMatrix.data());
+ }
+
+private:
+ std::vector<float> mMatrix;
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // COLORFILTER_H_
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index eb5878d95561..8c180da9c84f 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -54,6 +54,8 @@ public:
mImpl->updateChildren(std::move(updateFn));
}
+ void visit(std::function<void(const RenderNode&)> func) const { mImpl->visit(std::move(func)); }
+
[[nodiscard]] explicit operator bool() const {
return mImpl.get() != nullptr;
}
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index a18ba1c633b9..d21f07efe36a 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-X(Flush)
X(Save)
X(Restore)
X(SaveLayer)
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8191f5e6a83a..a958a091a830 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -15,6 +15,8 @@
*/
#include "FrameInfo.h"
+#include <gui/TraceUtils.h>
+
#include <cstring>
namespace android {
@@ -51,6 +53,30 @@ static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 23,
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+ mSkippedFrameReason.reset();
+}
+
+const char* toString(SkippedFrameReason reason) {
+ switch (reason) {
+ case SkippedFrameReason::DrawingOff:
+ return "DrawingOff";
+ case SkippedFrameReason::ContextIsStopped:
+ return "ContextIsStopped";
+ case SkippedFrameReason::NothingToDraw:
+ return "NothingToDraw";
+ case SkippedFrameReason::NoOutputTarget:
+ return "NoOutputTarget";
+ case SkippedFrameReason::NoBuffer:
+ return "NoBuffer";
+ case SkippedFrameReason::AlreadyDrawn:
+ return "AlreadyDrawn";
+ }
+}
+
+void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) {
+ ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason));
+ addFlag(FrameInfoFlags::SkippedFrame);
+ mSkippedFrameReason = reason;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index b15b6cb9a9ec..f7ad13978a30 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -16,15 +16,17 @@
#ifndef FRAMEINFO_H_
#define FRAMEINFO_H_
-#include "utils/Macros.h"
-
#include <cutils/compiler.h>
+#include <memory.h>
#include <utils/Timers.h>
#include <array>
-#include <memory.h>
+#include <optional>
#include <string>
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -186,8 +188,14 @@ public:
return mFrameInfo[static_cast<int>(index)];
}
+ void setSkippedFrameReason(SkippedFrameReason reason);
+ inline std::optional<SkippedFrameReason> getSkippedFrameReason() const {
+ return mSkippedFrameReason;
+ }
+
private:
int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
+ std::optional<SkippedFrameReason> mSkippedFrameReason;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 687e4dd324d3..59f21694fb77 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -148,7 +148,7 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
int fast_i = 0, janky_i = 0;
// Set the bottom of all the shapes to the baseline
for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
- if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+ if (mFrameSource[fi].getSkippedFrameReason()) {
continue;
}
float lineWidth = baseLineWidth;
@@ -181,7 +181,7 @@ void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex en
int janky_i = (mNumJankyRects - 1) * 4;
for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
- if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+ if (mFrameSource[fi].getSkippedFrameReason()) {
continue;
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index b7e99994355c..16de21def0e3 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -22,9 +22,11 @@
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GrDirectContext.h>
+#include <GrTypes.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkImage.h>
+#include <SkImageAndroid.h>
#include <SkImageInfo.h>
#include <SkRefCnt.h>
#include <gui/TraceUtils.h>
@@ -262,8 +264,9 @@ private:
}
sk_sp<SkImage> image =
- SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb);
- mGrContext->submit(true);
+ SkImages::TextureFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(),
+ ahb);
+ mGrContext->submit(GrSyncCpu::kYes);
uploadSucceeded = (image.get() != nullptr);
});
diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp
index ca1312e75f4c..21f4ca79b68c 100644
--- a/libs/hwui/MemoryPolicy.cpp
+++ b/libs/hwui/MemoryPolicy.cpp
@@ -28,7 +28,10 @@ namespace android::uirenderer {
constexpr static MemoryPolicy sDefaultMemoryPolicy;
constexpr static MemoryPolicy sPersistentOrSystemPolicy{
.contextTimeout = 10_s,
+ .minimumResourceRetention = 1_s,
+ .maximumResourceRetention = 10_s,
.useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
};
constexpr static MemoryPolicy sLowRamPolicy{
.useAlternativeUiHidden = true,
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
index 347daf34f52a..e10dda990dec 100644
--- a/libs/hwui/MemoryPolicy.h
+++ b/libs/hwui/MemoryPolicy.h
@@ -53,6 +53,8 @@ struct MemoryPolicy {
// The minimum amount of time to hold onto items in the resource cache
// The actual time used will be the max of this & when frames were actually rendered
nsecs_t minimumResourceRetention = 10_s;
+ // The maximum amount of time to hold onto items in the resource cache
+ nsecs_t maximumResourceRetention = 100000_s;
// If false, use only TRIM_UI_HIDDEN to drive background cache limits;
// If true, use all signals (such as all contexts are stopped) to drive the limits
bool useAlternativeUiHidden = true;
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 13e3c8e7bf77..764d1efcc8f4 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -19,6 +19,7 @@
#include <GrDirectContext.h>
#include <SkMesh.h>
+#include <include/gpu/ganesh/SkMeshGanesh.h>
#include <jni.h>
#include <log/log.h>
@@ -143,14 +144,26 @@ public:
}
if (mIsDirty || genId != mGenerationId) {
- auto vb = SkMesh::MakeVertexBuffer(
- context, reinterpret_cast<const void*>(mVertexBufferData.data()),
- mVertexBufferData.size());
+ auto vertexData = reinterpret_cast<const void*>(mVertexBufferData.data());
+#ifdef __ANDROID__
+ auto vb = SkMeshes::MakeVertexBuffer(context,
+ vertexData,
+ mVertexBufferData.size());
+#else
+ auto vb = SkMeshes::MakeVertexBuffer(vertexData,
+ mVertexBufferData.size());
+#endif
auto meshMode = SkMesh::Mode(mMode);
if (!mIndexBufferData.empty()) {
- auto ib = SkMesh::MakeIndexBuffer(
- context, reinterpret_cast<const void*>(mIndexBufferData.data()),
- mIndexBufferData.size());
+ auto indexData = reinterpret_cast<const void*>(mIndexBufferData.data());
+#ifdef __ANDROID__
+ auto ib = SkMeshes::MakeIndexBuffer(context,
+ indexData,
+ mIndexBufferData.size());
+#else
+ auto ib = SkMeshes::MakeIndexBuffer(indexData,
+ mIndexBufferData.size());
+#endif
mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
mBounds)
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 06aed63d8def..5e5eb4a51b35 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -20,7 +20,7 @@
#ifdef __ANDROID__
#include "HWUIProperties.sysprop.h"
#endif
-#include "SkTraceEventCommon.h"
+#include "src/core/SkTraceEventCommon.h"
#include <algorithm>
#include <cstdlib>
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 045de35c1d97..afe4c3896ed2 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -21,6 +21,7 @@
#include <SkCanvas.h>
#include <SkColorSpace.h>
#include <SkImage.h>
+#include <SkImageAndroid.h>
#include <SkImageInfo.h>
#include <SkMatrix.h>
#include <SkPaint.h>
@@ -29,6 +30,7 @@
#include <SkSamplingOptions.h>
#include <SkSurface.h>
#include "include/gpu/GpuTypes.h" // from Skia
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <gui/TraceUtils.h>
#include <private/android/AHardwareBufferHelpers.h>
#include <shaders/shaders.h>
@@ -108,7 +110,8 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy
sk_sp<SkColorSpace> colorSpace =
DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
sk_sp<SkImage> image =
- SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
+ SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType,
+ colorSpace);
if (!image.get()) {
return request->onCopyFinished(CopyResult::UnknownError);
@@ -171,16 +174,16 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy
SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
SkBitmap* bitmap = &skBitmap;
sk_sp<SkSurface> tmpSurface =
- SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), skgpu::Budgeted::kYes,
- bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
+ SkSurfaces::RenderTarget(mRenderThread.getGrContext(), skgpu::Budgeted::kYes,
+ bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
// if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
// attempt to do the intermediate rendering step in 8888
if (!tmpSurface.get()) {
SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
- tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- skgpu::Budgeted::kYes,
- tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
+ tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
+ skgpu::Budgeted::kYes,
+ tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
return request->onCopyFinished(CopyResult::UnknownError);
@@ -346,19 +349,19 @@ bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect*
* a scaling issue (b/62262733) that was encountered when sampling from an EGLImage into a
* software buffer.
*/
- sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- skgpu::Budgeted::kYes,
- bitmap->info(),
- 0,
- kTopLeft_GrSurfaceOrigin, nullptr);
+ sk_sp<SkSurface> tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
+ skgpu::Budgeted::kYes,
+ bitmap->info(),
+ 0,
+ kTopLeft_GrSurfaceOrigin, nullptr);
// if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
// attempt to do the intermediate rendering step in 8888
if (!tmpSurface.get()) {
SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
- tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- skgpu::Budgeted::kYes,
- tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
+ tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
+ skgpu::Budgeted::kYes,
+ tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
return false;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 924fbd659824..71f47e92e055 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -36,6 +36,7 @@
#include "SkImageFilter.h"
#include "SkImageInfo.h"
#include "SkLatticeIter.h"
+#include "SkMesh.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkRRect.h"
@@ -49,6 +50,7 @@
#include "effects/GainmapRenderer.h"
#include "include/gpu/GpuTypes.h" // from Skia
#include "include/gpu/GrDirectContext.h"
+#include "include/gpu/ganesh/SkMeshGanesh.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/FunctorDrawable.h"
#ifdef __ANDROID__
@@ -107,11 +109,6 @@ struct Op {
};
static_assert(sizeof(Op) == 4, "");
-struct Flush final : Op {
- static const auto kType = Type::Flush;
- void draw(SkCanvas* c, const SkMatrix&) const { c->flush(); }
-};
-
struct Save final : Op {
static const auto kType = Type::Save;
void draw(SkCanvas* c, const SkMatrix&) const { c->save(); }
@@ -532,12 +529,13 @@ struct DrawSkMesh final : Op {
mutable bool isGpuBased;
mutable GrDirectContext::DirectContextID contextId;
void draw(SkCanvas* c, const SkMatrix&) const {
+#ifdef __ANDROID__
GrDirectContext* directContext = c->recordingContext()->asDirectContext();
GrDirectContext::DirectContextID id = directContext->directContextID();
if (!isGpuBased || contextId != id) {
sk_sp<SkMesh::VertexBuffer> vb =
- SkMesh::CopyVertexBuffer(directContext, cpuMesh.refVertexBuffer());
+ SkMeshes::CopyVertexBuffer(directContext, cpuMesh.refVertexBuffer());
if (!cpuMesh.indexBuffer()) {
gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(),
cpuMesh.vertexOffset(), cpuMesh.refUniforms(),
@@ -545,7 +543,7 @@ struct DrawSkMesh final : Op {
.mesh;
} else {
sk_sp<SkMesh::IndexBuffer> ib =
- SkMesh::CopyIndexBuffer(directContext, cpuMesh.refIndexBuffer());
+ SkMeshes::CopyIndexBuffer(directContext, cpuMesh.refIndexBuffer());
gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb,
cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib,
cpuMesh.indexCount(), cpuMesh.indexOffset(),
@@ -558,6 +556,9 @@ struct DrawSkMesh final : Op {
}
c->drawMesh(gpuMesh, blender, paint);
+#else
+ c->drawMesh(cpuMesh, blender, paint);
+#endif
}
};
@@ -675,12 +676,11 @@ public:
// because the webview functor still doesn't respect the canvas clip stack.
const SkIRect deviceBounds = c->getDeviceClipBounds();
if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) {
- GrRecordingContext* directContext = c->recordingContext();
mLayerImageInfo =
c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height());
- mLayerSurface = SkSurface::MakeRenderTarget(directContext, skgpu::Budgeted::kYes,
- mLayerImageInfo, 0,
- kTopLeft_GrSurfaceOrigin, nullptr);
+ // SkCanvas::makeSurface returns a new surface that will be GPU-backed if
+ // canvas was also.
+ mLayerSurface = c->makeSurface(mLayerImageInfo);
}
SkCanvas* layerCanvas = mLayerSurface->getCanvas();
@@ -752,10 +752,6 @@ inline void DisplayListData::map(const Fn fns[], Args... args) const {
}
}
-void DisplayListData::flush() {
- this->push<Flush>(0);
-}
-
void DisplayListData::save() {
this->push<Save>(0);
}
@@ -1047,10 +1043,6 @@ sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfa
return nullptr;
}
-void RecordingCanvas::onFlush() {
- fDL->flush();
-}
-
void RecordingCanvas::willSave() {
mSaveCount++;
fDL->save();
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 1f4ba5d6d557..4f54ee286a56 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -127,8 +127,6 @@ public:
private:
friend class RecordingCanvas;
- void flush();
-
void save();
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags);
void saveBehind(const SkRect*);
@@ -208,8 +206,6 @@ public:
void willRestore() override;
bool onDoSaveBehind(const SkRect*) override;
- void onFlush() override;
-
void didConcat44(const SkM44&) override;
void didSetM44(const SkM44&) override;
void didScale(SkScalar, SkScalar) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 1c39db3a31bb..d28bb499c907 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -28,16 +28,20 @@
#include "DamageAccumulator.h"
#include "pipeline/skia/SkiaDisplayList.h"
#endif
+#include <SkPathOps.h>
#include <gui/TraceUtils.h>
-#include "utils/MathUtils.h"
-#include "utils/StringUtils.h"
+#include <ui/FatVector.h>
-#include <SkPathOps.h>
#include <algorithm>
#include <atomic>
#include <sstream>
#include <string>
-#include <ui/FatVector.h>
+
+#ifdef __ANDROID__
+#include "include/gpu/ganesh/SkImageGanesh.h"
+#endif
+#include "utils/MathUtils.h"
+#include "utils/StringUtils.h"
namespace android {
namespace uirenderer {
@@ -109,6 +113,13 @@ void RenderNode::output(std::ostream& output, uint32_t level) {
output << std::endl;
}
+void RenderNode::visit(std::function<void(const RenderNode&)> func) const {
+ func(*this);
+ if (mDisplayList) {
+ mDisplayList.visit(func);
+ }
+}
+
int RenderNode::getUsageSize() {
int size = sizeof(RenderNode);
size += mStagingDisplayList.getUsedSize();
@@ -260,6 +271,12 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
pushStagingDisplayListChanges(observer, info);
}
+ // always damageSelf when filtering backdrop content, or else the BackdropFilterDrawable will
+ // get a wrong snapshot of previous content.
+ if (mProperties.layerProperties().getBackdropImageFilter()) {
+ damageSelf(info);
+ }
+
if (mDisplayList) {
info.out.hasFunctors |= mDisplayList.hasFunctor();
mHasHolePunches = mDisplayList.hasHolePunches();
@@ -357,13 +374,18 @@ std::optional<RenderNode::SnapshotResult> RenderNode::updateSnapshotIfRequired(
mImageFilterClipBounds != clipBounds ||
mTargetImageFilterLayerSurfaceGenerationId != layerSurfaceGenerationId) {
// Otherwise create a new snapshot with the given filter and snapshot
- mSnapshotResult.snapshot =
- snapshot->makeWithFilter(context,
- imageFilter,
- subset,
- clipBounds,
- &mSnapshotResult.outSubset,
- &mSnapshotResult.outOffset);
+#ifdef __ANDROID__
+ if (context) {
+ mSnapshotResult.snapshot = SkImages::MakeWithFilter(
+ context, snapshot, imageFilter, subset, clipBounds, &mSnapshotResult.outSubset,
+ &mSnapshotResult.outOffset);
+ } else
+#endif
+ {
+ mSnapshotResult.snapshot = SkImages::MakeWithFilter(
+ snapshot, imageFilter, subset, clipBounds, &mSnapshotResult.outSubset,
+ &mSnapshotResult.outOffset);
+ }
mTargetImageFilter = sk_ref_sp(imageFilter);
mImageFilterClipBounds = clipBounds;
mTargetImageFilterLayerSurfaceGenerationId = layerSurfaceGenerationId;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 572635a9bd45..c959db37474b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -129,10 +129,6 @@ public:
StretchMask& getStretchMask() { return mStretchMask; }
- VirtualLightRefBase* getUserContext() const { return mUserContext.get(); }
-
- void setUserContext(VirtualLightRefBase* context) { mUserContext = context; }
-
bool isPropertyFieldDirty(DirtyPropertyMask field) const {
return mDirtyPropertyFields & field;
}
@@ -215,6 +211,8 @@ public:
void output(std::ostream& output, uint32_t level);
+ void visit(std::function<void(const RenderNode&)>) const;
+
void setUsageHint(UsageHint usageHint) { mUsageHint = usageHint; }
UsageHint usageHint() const { return mUsageHint; }
@@ -222,6 +220,7 @@ public:
int64_t uniqueId() const { return mUniqueId; }
void setIsTextureView() { mIsTextureView = true; }
+ bool isTextureView() const { return mIsTextureView; }
void markDrawStart(SkCanvas& canvas);
void markDrawEnd(SkCanvas& canvas);
@@ -248,7 +247,6 @@ private:
const int64_t mUniqueId;
String8 mName;
- sp<VirtualLightRefBase> mUserContext;
uint32_t mDirtyPropertyFields;
RenderProperties mProperties;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 0589f136b666..c5371236b9cf 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -55,6 +55,12 @@ bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
return true;
}
+bool LayerProperties::setBackdropImageFilter(SkImageFilter* imageFilter) {
+ if (mBackdropImageFilter.get() == imageFilter) return false;
+ mBackdropImageFilter = sk_ref_sp(imageFilter);
+ return true;
+}
+
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -70,6 +76,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
setImageFilter(other.getImageFilter());
+ setBackdropImageFilter(other.getBackdropImageFilter());
mStretchEffect = other.mStretchEffect;
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 064ba7aee107..e358b57f6fe1 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -97,8 +97,12 @@ public:
bool setImageFilter(SkImageFilter* imageFilter);
+ bool setBackdropImageFilter(SkImageFilter* imageFilter);
+
SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+ SkImageFilter* getBackdropImageFilter() const { return mBackdropImageFilter.get(); }
+
const StretchEffect& getStretchEffect() const { return mStretchEffect; }
StretchEffect& mutableStretchEffect() { return mStretchEffect; }
@@ -129,6 +133,7 @@ private:
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
sk_sp<SkImageFilter> mImageFilter;
+ sk_sp<SkImageFilter> mBackdropImageFilter;
StretchEffect mStretchEffect;
};
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index b785989f35cb..ced02241ffe2 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -175,7 +175,7 @@ protected:
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
- void onFilterPaint(Paint& paint);
+ virtual void onFilterPaint(Paint& paint);
Paint filterPaint(const Paint& src) {
Paint dst(src);
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index b58f517834a3..c67b135855f7 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -18,9 +18,8 @@
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
-#include "include/private/SkFixed.h"
-#include "src/core/SkTSearch.h"
+#include <cstdlib>
#include <log/log.h>
typedef int Dot14;
@@ -41,18 +40,18 @@ static inline Dot14 pin_and_convert(float x) {
if (x <= 0) {
return 0;
}
- if (x >= SK_Scalar1) {
+ if (x >= 1.0f) {
return Dot14_ONE;
}
- return SkScalarToFixed(x) >> 2;
+ return static_cast<Dot14>(x * Dot14_ONE);
}
static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) {
// pin to the unit-square, and convert to 2.14
Dot14 x = pin_and_convert(value);
- if (x == 0) return 0;
- if (x == Dot14_ONE) return SK_Scalar1;
+ if (x == 0) return 0.0f;
+ if (x == Dot14_ONE) return 1.0f;
Dot14 b = pin_and_convert(bx);
Dot14 c = pin_and_convert(cx);
@@ -84,7 +83,7 @@ static float SkUnitCubicInterp(float value, float bx, float by, float cx, float
A = 3 * b;
B = 3 * (c - 2 * b);
C = 3 * (b - c) + Dot14_ONE;
- return SkFixedToScalar(eval_cubic(t, A, B, C) << 2);
+ return Dot14ToFloat(eval_cubic(t, A, B, C));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -104,7 +103,7 @@ void SkiaInterpolatorBase::reset(int elemCount, int frameCount) {
fFlags = 0;
fElemCount = static_cast<uint8_t>(elemCount);
fFrameCount = static_cast<int16_t>(frameCount);
- fRepeat = SK_Scalar1;
+ fRepeat = 1.0f;
if (fStorage) {
free(fStorage);
fStorage = nullptr;
@@ -136,17 +135,46 @@ bool SkiaInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const
float SkiaInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime,
const float blend[4]) {
- SkASSERT(time > prevTime && time < nextTime);
+ LOG_FATAL_IF(time < prevTime || time > nextTime);
float t = (float)(time - prevTime) / (float)(nextTime - prevTime);
return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
}
+// Returns the index of where the item is or the bit not of the index
+// where the item should go in order to keep arr sorted in ascending order.
+int SkiaInterpolatorBase::binarySearch(const SkTimeCode* arr, int count, SkMSec target) {
+ if (count <= 0) {
+ return ~0;
+ }
+
+ int lo = 0;
+ int hi = count - 1;
+
+ while (lo < hi) {
+ int mid = (hi + lo) / 2;
+ SkMSec elem = arr[mid].fTime;
+ if (elem == target) {
+ return mid;
+ } else if (elem < target) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+ // Check to see if target is greater or less than where we stopped
+ if (target < arr[lo].fTime) {
+ return ~lo;
+ }
+ // e.g. it should go at the end.
+ return ~(lo + 1);
+}
+
SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T, int* indexPtr,
bool* exactPtr) const {
- SkASSERT(fFrameCount > 0);
+ LOG_FATAL_IF(fFrameCount <= 0);
Result result = kNormal_Result;
- if (fRepeat != SK_Scalar1) {
+ if (fRepeat != 1.0f) {
SkMSec startTime = 0, endTime = 0; // initialize to avoid warning
this->getDuration(&startTime, &endTime);
SkMSec totalTime = endTime - startTime;
@@ -168,10 +196,8 @@ SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T
time = offsetTime + startTime;
}
- int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, sizeof(SkTimeCode));
-
+ int index = SkiaInterpolatorBase::binarySearch(fTimes, fFrameCount, time);
bool exact = true;
-
if (index < 0) {
index = ~index;
if (index == 0) {
@@ -184,10 +210,11 @@ SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T
}
result = kFreezeEnd_Result;
} else {
+ // Need to interpolate between two frames.
exact = false;
}
}
- SkASSERT(index < fFrameCount);
+ LOG_FATAL_IF(index >= fFrameCount);
const SkTimeCode* nextTime = &fTimes[index];
SkMSec nextT = nextTime[0].fTime;
if (exact) {
@@ -207,7 +234,7 @@ SkiaInterpolator::SkiaInterpolator() {
}
SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) {
- SkASSERT(elemCount > 0);
+ LOG_FATAL_IF(elemCount <= 0);
this->reset(elemCount, frameCount);
}
@@ -221,21 +248,19 @@ void SkiaInterpolator::reset(int elemCount, int frameCount) {
fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount);
}
-#define SK_Fixed1Third (SK_Fixed1 / 3)
-#define SK_Fixed2Third (SK_Fixed1 * 2 / 3)
-
static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f};
bool SkiaInterpolator::setKeyFrame(int index, SkMSec time, const float values[],
const float blend[4]) {
- SkASSERT(values != nullptr);
+ LOG_FATAL_IF(values == nullptr);
if (blend == nullptr) {
blend = gIdentityBlend;
}
- bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode));
- SkASSERT(success);
+ // Verify the time should go after all the frames before index
+ bool success = ~index == SkiaInterpolatorBase::binarySearch(fTimes, index, time);
+ LOG_FATAL_IF(!success);
if (success) {
SkTimeCode* timeCode = &fTimes[index];
timeCode->fTime = time;
@@ -257,7 +282,7 @@ SkiaInterpolator::Result SkiaInterpolator::timeToValues(SkMSec time, float value
if (exact) {
memcpy(values, nextSrc, fElemCount * sizeof(float));
} else {
- SkASSERT(index > 0);
+ LOG_FATAL_IF(index <= 0);
const float* prevSrc = nextSrc - fElemCount;
diff --git a/libs/hwui/SkiaInterpolator.h b/libs/hwui/SkiaInterpolator.h
index 9422cb526a8f..62e6c1e33e40 100644
--- a/libs/hwui/SkiaInterpolator.h
+++ b/libs/hwui/SkiaInterpolator.h
@@ -68,14 +68,16 @@ protected:
enum Flags { kMirror = 1, kReset = 2, kHasBlend = 4 };
static float ComputeRelativeT(uint32_t time, uint32_t prevTime, uint32_t nextTime,
const float blend[4] = nullptr);
- int16_t fFrameCount;
- uint8_t fElemCount;
- uint8_t fFlags;
- float fRepeat;
struct SkTimeCode {
uint32_t fTime;
float fBlend[4];
};
+ static int binarySearch(const SkTimeCode* arr, int count, uint32_t target);
+
+ int16_t fFrameCount;
+ uint8_t fElemCount;
+ uint8_t fFlags;
+ float fRepeat;
SkTimeCode* fTimes; // pointer into fStorage
void* fStorage;
};
diff --git a/libs/hwui/SkiaWrapper.h b/libs/hwui/SkiaWrapper.h
new file mode 100644
index 000000000000..bd0e35aadbb4
--- /dev/null
+++ b/libs/hwui/SkiaWrapper.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SKIA_WRAPPER_H_
+#define SKIA_WRAPPER_H_
+
+#include <SkRefCnt.h>
+#include <utils/RefBase.h>
+
+namespace android::uirenderer {
+
+template <typename T>
+class SkiaWrapper : public VirtualLightRefBase {
+public:
+ sk_sp<T> getInstance() {
+ if (mInstance != nullptr && shouldDiscardInstance()) {
+ mInstance = nullptr;
+ }
+
+ if (mInstance == nullptr) {
+ mInstance = createInstance();
+ mGenerationId++;
+ }
+ return mInstance;
+ }
+
+ virtual bool shouldDiscardInstance() const { return false; }
+
+ void discardInstance() { mInstance = nullptr; }
+
+ [[nodiscard]] int32_t getGenerationId() const { return mGenerationId; }
+
+protected:
+ virtual sk_sp<T> createInstance() = 0;
+
+private:
+ sk_sp<T> mInstance = nullptr;
+ int32_t mGenerationId = 0;
+};
+
+} // namespace android::uirenderer
+
+#endif // SKIA_WRAPPER_H_
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/hwui/SkippedFrameInfo.h
index ee55eca31072..de56d9a26982 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
+++ b/libs/hwui/SkippedFrameInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.bubble
+#pragma once
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+namespace android::uirenderer {
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) :
- DragToDismissBubbleScreenTest(flicker)
+enum class SkippedFrameReason {
+ DrawingOff,
+ ContextIsStopped,
+ NothingToDraw,
+ NoOutputTarget,
+ NoBuffer,
+ AlreadyDrawn,
+};
+
+} /* namespace android::uirenderer */
diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp
index 974a5d05aa84..ae29edf535a2 100644
--- a/libs/hwui/Tonemapper.cpp
+++ b/libs/hwui/Tonemapper.cpp
@@ -20,6 +20,7 @@
#include <log/log.h>
// libshaders only exists on Android devices
#ifdef __ANDROID__
+#include <renderthread/CanvasContext.h>
#include <shaders/shaders.h>
#endif
@@ -53,8 +54,17 @@ static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearE
ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
+ auto colorTransform = android::mat4();
+ const auto* context = renderthread::CanvasContext::getActiveContext();
+ if (context) {
+ const auto ratio = context->targetSdrHdrRatio();
+ if (ratio > 1.0f) {
+ colorTransform = android::mat4::scale(vec4(ratio, ratio, ratio, 1.f));
+ }
+ }
+
const auto uniforms =
- shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance,
+ shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance,
currentDisplayLuminanceNits, maxLuminance);
for (const auto& uniform : uniforms) {
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 2bff9cb74fa7..ea25f68d7170 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,14 +16,16 @@
#pragma once
-#include "Properties.h"
-#include "utils/Macros.h"
-
#include <utils/Timers.h>
-#include "SkSize.h"
+#include <optional>
#include <string>
+#include "Properties.h"
+#include "SkSize.h"
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -110,13 +112,13 @@ public:
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
bool requiresUiRedraw = false;
- // This is set to true if draw() can be called this frame
- // false means that we must delay until the next vsync pulse as frame
+ // This is set to nullopt if draw() can be called this frame
+ // A value means that we must delay until the next vsync pulse as frame
// production is outrunning consumption
- // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+ // NOTE that if this has a value CanvasContext will set either requiresUiRedraw
// *OR* will post itself for the next vsync automatically, use this
// only to avoid calling draw()
- bool canDrawThisFrame = true;
+ std::optional<SkippedFrameReason> skippedFrameReason;
// Sentinel for animatedImageDelay meaning there is no need to post such
// a message.
static constexpr nsecs_t kNoAnimatedImageDelay = -1;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
new file mode 100644
index 000000000000..d0d3c5e7ece1
--- /dev/null
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.graphics.hwui.flags"
+
+flag {
+ name: "limited_hdr"
+ namespace: "core_graphics"
+ description: "API to enable apps to restrict the amount of HDR headroom that is used"
+ bug: "234181960"
+}
+
+flag {
+ name: "hdr_10bit_plus"
+ namespace: "core_graphics"
+ description: "Use 10101010 and FP16 formats for HDR-UI when available"
+ bug: "284159488"
+}
+
+flag {
+ name: "gainmap_animations"
+ namespace: "core_graphics"
+ description: "APIs to help enable animations involving gainmaps"
+ bug: "296482289"
+}
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index c442a7b1d17c..c80a9b4ae97f 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "Bitmap"
#include <log/log.h>
#include "android/graphics/bitmap.h"
diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp
index 905b123076a2..19f726a31b33 100644
--- a/libs/hwui/apex/android_canvas.cpp
+++ b/libs/hwui/apex/android_canvas.cpp
@@ -45,9 +45,9 @@ static bool convert(const ANativeWindow_Buffer* buffer,
SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs);
size_t rowBytes = buffer->stride * imageInfo.bytesPerPixel();
- // If SkSurface::MakeRasterDirect fails then we should as well as we will not be able to
+ // If SkSurfaces::WrapPixels fails then we should as well as we will not be able to
// draw into the canvas.
- sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(imageInfo, buffer->bits, rowBytes);
+ sk_sp<SkSurface> surface = SkSurfaces::WrapPixels(imageInfo, buffer->bits, rowBytes);
if (surface.get() != nullptr) {
if (outBitmap) {
outBitmap->setInfo(imageInfo, rowBytes);
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 09ae7e78fe23..883f273b5d3d 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -25,9 +25,6 @@
#include <sys/cdefs.h>
#include <vulkan/vulkan.h>
-#undef LOG_TAG
-#define LOG_TAG "AndroidGraphicsJNI"
-
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp
index 651d526511ef..3ebf7d19202d 100644
--- a/libs/hwui/effects/GainmapRenderer.cpp
+++ b/libs/hwui/effects/GainmapRenderer.cpp
@@ -244,11 +244,18 @@ private:
// This can happen if a BitmapShader is used on multiple canvas', such as a
// software + hardware canvas, which is otherwise valid as SkShader is "immutable"
std::lock_guard _lock(mUniformGuard);
- const float Wunclamped = (sk_float_log(targetHdrSdrRatio) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr)) /
- (sk_float_log(mGainmapInfo.fDisplayRatioHdr) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr));
- const float W = std::max(std::min(Wunclamped, 1.f), 0.f);
+ // Compute the weight parameter that will be used to blend between the images.
+ float W = 0.f;
+ if (targetHdrSdrRatio > mGainmapInfo.fDisplayRatioSdr) {
+ if (targetHdrSdrRatio < mGainmapInfo.fDisplayRatioHdr) {
+ W = (sk_float_log(targetHdrSdrRatio) -
+ sk_float_log(mGainmapInfo.fDisplayRatioSdr)) /
+ (sk_float_log(mGainmapInfo.fDisplayRatioHdr) -
+ sk_float_log(mGainmapInfo.fDisplayRatioSdr));
+ } else {
+ W = 1.f;
+ }
+ }
mBuilder.uniform("W") = W;
uniforms = mBuilder.uniforms();
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 8049dc946c9e..27773a60355a 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -111,7 +111,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
{
std::unique_lock lock{mImageLock};
snap.mDurationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame());
- snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+ snap.mPic = mSkAnimatedImage->makePictureSnapshot();
}
return snap;
@@ -123,7 +123,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
{
std::unique_lock lock{mImageLock};
mSkAnimatedImage->reset();
- snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+ snap.mPic = mSkAnimatedImage->makePictureSnapshot();
snap.mDurationMS = currentFrameDuration();
}
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 92d875bf7f1e..8344a86923ee 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -43,12 +43,15 @@
#include <SkColor.h>
#include <SkEncodedImageFormat.h>
#include <SkHighContrastFilter.h>
-#include <SkImageEncoder.h>
+#include <SkImage.h>
+#include <SkImageAndroid.h>
#include <SkImagePriv.h>
#include <SkJpegGainmapEncoder.h>
#include <SkPixmap.h>
#include <SkRect.h>
#include <SkStream.h>
+#include <SkJpegEncoder.h>
+#include <SkPngEncoder.h>
#include <SkWebpEncoder.h>
#include <limits>
@@ -296,7 +299,8 @@ Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes
mPixelStorage.hardware.size = AHardwareBuffer_getAllocationSize(buffer);
AHardwareBuffer_acquire(buffer);
setImmutable(); // HW bitmaps are always immutable
- mImage = SkImage::MakeFromAHardwareBuffer(buffer, mInfo.alphaType(), mInfo.refColorSpace());
+ mImage = SkImages::DeferredFromAHardwareBuffer(buffer, mInfo.alphaType(),
+ mInfo.refColorSpace());
}
#endif
@@ -407,7 +411,12 @@ sk_sp<SkImage> Bitmap::makeImage() {
// Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
// internally and ~Bitmap won't be invoked.
// TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
+#ifdef __ANDROID__
+ // pinnable images are only supported with the Ganesh GPU backend compiled in.
+ image = SkImages::PinnableRasterFromBitmap(skiaBitmap);
+#else
image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
+#endif
}
return image;
}
@@ -528,17 +537,25 @@ bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
return false;
}
- SkEncodedImageFormat fm;
switch (format) {
- case JavaCompressFormat::Jpeg:
- fm = SkEncodedImageFormat::kJPEG;
- break;
+ case JavaCompressFormat::Jpeg: {
+ SkJpegEncoder::Options options;
+ options.fQuality = quality;
+ return SkJpegEncoder::Encode(stream, bitmap.pixmap(), options);
+ }
case JavaCompressFormat::Png:
- fm = SkEncodedImageFormat::kPNG;
- break;
- case JavaCompressFormat::Webp:
- fm = SkEncodedImageFormat::kWEBP;
- break;
+ return SkPngEncoder::Encode(stream, bitmap.pixmap(), {});
+ case JavaCompressFormat::Webp: {
+ SkWebpEncoder::Options options;
+ if (quality >= 100) {
+ options.fCompression = SkWebpEncoder::Compression::kLossless;
+ options.fQuality = 75; // This is effort to compress
+ } else {
+ options.fCompression = SkWebpEncoder::Compression::kLossy;
+ options.fQuality = quality;
+ }
+ return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
+ }
case JavaCompressFormat::WebpLossy:
case JavaCompressFormat::WebpLossless: {
SkWebpEncoder::Options options;
@@ -548,8 +565,6 @@ bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
}
}
-
- return SkEncodeImage(stream, bitmap, fm, quality);
}
sp<uirenderer::Gainmap> Bitmap::gainmap() const {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index cd8af3d933b1..2351797ac787 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -151,7 +151,7 @@ void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const fl
memcpy(outPositions, positions, sizeof(float) * 2 * glyphCount);
};
- const minikin::MinikinFont* minikinFont = font.typeface().get();
+ const minikin::MinikinFont* minikinFont = font.baseTypeface().get();
SkFont* skfont = &copied.getSkFont();
MinikinFontSkia::populateSkFont(skfont, minikinFont, minikin::FontFakery());
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 701a87f0cce4..588463c49497 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -43,9 +43,6 @@
#include <memory>
-#undef LOG_TAG
-#define LOG_TAG "ImageDecoder"
-
using namespace android;
sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index e359145feef7..bcfb4c89036d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -84,7 +84,8 @@ void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const
float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
- size_t count, size_t bufSize, float* advances) {
+ size_t count, size_t bufSize, float* advances,
+ minikin::MinikinRect* bounds) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
const minikin::U16StringPiece textBuf(buf, bufSize);
const minikin::Range range(start, start + count);
@@ -92,7 +93,7 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
- endHyphen, advances);
+ endHyphen, advances, bounds);
}
minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 009b84b140ea..61bc881faa54 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -51,10 +51,9 @@ public:
static void getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out);
- static float measureText(const Paint* paint, minikin::Bidi bidiFlags,
- const Typeface* typeface, const uint16_t* buf,
- size_t start, size_t count, size_t bufSize,
- float* advances);
+ static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
+ const uint16_t* buf, size_t start, size_t count, size_t bufSize,
+ float* advances, minikin::MinikinRect* bounds);
static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
@@ -76,7 +75,7 @@ public:
size_t start = 0;
size_t nGlyphs = layout.nGlyphs();
for (size_t i = 0; i < nGlyphs; i++) {
- const minikin::MinikinFont* nextFont = layout.getFont(i)->typeface().get();
+ const minikin::MinikinFont* nextFont = layout.typeface(i).get();
if (i > 0 && nextFont != curFont) {
SkFont* skfont = &paint->getSkFont();
MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 3c67edc9a428..b63ee1bd3d98 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -140,9 +140,8 @@ Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::Font
const minikin::FontStyle defaultStyle;
const minikin::MinikinFont* mf =
- families.empty()
- ? nullptr
- : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
+ families.empty() ? nullptr
+ : families[0]->getClosestMatch(defaultStyle).typeface().get();
if (mf != nullptr) {
SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
const SkFontStyle& style = skTypeface->fontStyle();
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index a7f5aa83e624..90b1da846205 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-#include "GraphicsJNI.h"
-#include "ImageDecoder.h"
-#include "Utils.h"
-
#include <SkAndroidCodec.h>
#include <SkAnimatedImage.h>
#include <SkColorFilter.h>
@@ -27,10 +23,15 @@
#include <SkRect.h>
#include <SkRefCnt.h>
#include <hwui/AnimatedImageDrawable.h>
-#include <hwui/ImageDecoder.h>
#include <hwui/Canvas.h>
+#include <hwui/ImageDecoder.h>
#include <utils/Looper.h>
+#include "ColorFilter.h"
+#include "GraphicsJNI.h"
+#include "ImageDecoder.h"
+#include "Utils.h"
+
using namespace android;
static jclass gAnimatedImageDrawableClass;
@@ -145,8 +146,9 @@ static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlo
static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jlong nativeFilter) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
- drawable->setStagingColorFilter(sk_ref_sp(filter));
+ auto filter = uirenderer::ColorFilter::fromJava(nativeFilter);
+ auto skColorFilter = filter != nullptr ? filter->getInstance() : sk_sp<SkColorFilter>();
+ drawable->setStagingColorFilter(skColorFilter);
}
static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 6ee7576651f2..9e21f860ce21 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -1,5 +1,3 @@
-#undef LOG_TAG
-#define LOG_TAG "Bitmap"
// #define LOG_NDEBUG 0
#include "Bitmap.h"
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 8abcd9a59122..3d0a53440bfb 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -1,6 +1,3 @@
-#undef LOG_TAG
-#define LOG_TAG "BitmapFactory"
-
#include "BitmapFactory.h"
#include <Gainmap.h>
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 740988f77270..ea5c14486ea4 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "BitmapRegionDecoder"
-
#include "BitmapRegionDecoder.h"
#include <HardwareBitmapUploader.h>
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index 4bd7ef47b871..0b95148d3c82 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -15,20 +15,21 @@
** limitations under the License.
*/
-#include "GraphicsJNI.h"
+#include "ColorFilter.h"
+#include "GraphicsJNI.h"
#include "SkBlendMode.h"
-#include "SkColorFilter.h"
-#include "SkColorMatrixFilter.h"
namespace android {
using namespace uirenderer;
-class SkColorFilterGlue {
+class ColorFilterGlue {
public:
- static void SafeUnref(SkColorFilter* filter) {
- SkSafeUnref(filter);
+ static void SafeUnref(ColorFilter* filter) {
+ if (filter) {
+ filter->decStrong(nullptr);
+ }
}
static jlong GetNativeFinalizer(JNIEnv*, jobject) {
@@ -36,41 +37,75 @@ public:
}
static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
- SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
- return reinterpret_cast<jlong>(SkColorFilters::Blend(srcColor, mode).release());
+ auto mode = static_cast<SkBlendMode>(modeHandle);
+ auto* blendModeFilter = new BlendModeColorFilter(srcColor, mode);
+ blendModeFilter->incStrong(nullptr);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(blendModeFilter));
}
static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
- return reinterpret_cast<jlong>(SkColorMatrixFilter::MakeLightingFilter(mul, add).release());
+ auto* lightingFilter = new LightingFilter(mul, add);
+ lightingFilter->incStrong(nullptr);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(lightingFilter));
}
- static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
- float matrix[20];
- env->GetFloatArrayRegion(jarray, 0, 20, matrix);
+ static void SetLightingFilterMul(JNIEnv* env, jobject, jlong lightingFilterPtr, jint mul) {
+ auto* filter = reinterpret_cast<LightingFilter*>(lightingFilterPtr);
+ if (filter) {
+ filter->setMul(mul);
+ }
+ }
+
+ static void SetLightingFilterAdd(JNIEnv* env, jobject, jlong lightingFilterPtr, jint add) {
+ auto* filter = reinterpret_cast<LightingFilter*>(lightingFilterPtr);
+ if (filter) {
+ filter->setAdd(add);
+ }
+ }
+
+ static std::vector<float> getMatrixFromJFloatArray(JNIEnv* env, jfloatArray jarray) {
+ std::vector<float> matrix(20);
+ // float matrix[20];
+ env->GetFloatArrayRegion(jarray, 0, 20, matrix.data());
// java biases the translates by 255, so undo that before calling skia
matrix[ 4] *= (1.0f/255);
matrix[ 9] *= (1.0f/255);
matrix[14] *= (1.0f/255);
matrix[19] *= (1.0f/255);
- return reinterpret_cast<jlong>(SkColorFilters::Matrix(matrix).release());
+ return matrix;
+ }
+
+ static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
+ std::vector<float> matrix = getMatrixFromJFloatArray(env, jarray);
+ auto* colorMatrixColorFilter = new ColorMatrixColorFilter(std::move(matrix));
+ colorMatrixColorFilter->incStrong(nullptr);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(colorMatrixColorFilter));
+ }
+
+ static void SetColorMatrix(JNIEnv* env, jobject, jlong colorMatrixColorFilterPtr,
+ jfloatArray jarray) {
+ auto* filter = reinterpret_cast<ColorMatrixColorFilter*>(colorMatrixColorFilterPtr);
+ if (filter) {
+ filter->setMatrix(getMatrixFromJFloatArray(env, jarray));
+ }
}
};
static const JNINativeMethod colorfilter_methods[] = {
- {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer }
-};
+ {"nativeGetFinalizer", "()J", (void*)ColorFilterGlue::GetNativeFinalizer}};
static const JNINativeMethod blendmode_methods[] = {
- { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter },
+ {"native_CreateBlendModeFilter", "(II)J", (void*)ColorFilterGlue::CreateBlendModeFilter},
};
static const JNINativeMethod lighting_methods[] = {
- { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter },
-};
+ {"native_CreateLightingFilter", "(II)J", (void*)ColorFilterGlue::CreateLightingFilter},
+ {"native_SetLightingFilterAdd", "(JI)V", (void*)ColorFilterGlue::SetLightingFilterAdd},
+ {"native_SetLightingFilterMul", "(JI)V", (void*)ColorFilterGlue::SetLightingFilterMul}};
static const JNINativeMethod colormatrix_methods[] = {
- { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter },
-};
+ {"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter},
+ {"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}};
int register_android_graphics_ColorFilter(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 15e529e169fc..a66d3b860ade 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -1,11 +1,11 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "SkData.h"
-#include "SkMalloc.h"
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
#include "Utils.h"
+#include <cstdlib>
#include <nativehelper/JNIHelp.h>
#include <log/log.h>
#include <memory>
@@ -177,6 +177,10 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray s
return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions);
}
+static void free_pointer_skproc(const void* ptr, void*) {
+ free((void*)ptr);
+}
+
sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray storage) {
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, inputStream, storage));
if (!stream) {
@@ -186,19 +190,31 @@ sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray s
size_t bufferSize = 4096;
size_t streamLen = 0;
size_t len;
- char* data = (char*)sk_malloc_throw(bufferSize);
+ char* data = (char*)malloc(bufferSize);
+ LOG_ALWAYS_FATAL_IF(!data);
while ((len = stream->read(data + streamLen,
bufferSize - streamLen)) != 0) {
streamLen += len;
if (streamLen == bufferSize) {
bufferSize *= 2;
- data = (char*)sk_realloc_throw(data, bufferSize);
+ data = (char*)realloc(data, bufferSize);
+ LOG_ALWAYS_FATAL_IF(!data);
}
}
- data = (char*)sk_realloc_throw(data, streamLen);
-
- return SkData::MakeFromMalloc(data, streamLen);
+ if (streamLen == 0) {
+ // realloc with size 0 is unspecified behavior in C++11
+ free(data);
+ data = nullptr;
+ } else {
+ // Trim down the buffer to the actual size of the data.
+ LOG_FATAL_IF(streamLen > bufferSize);
+ data = (char*)realloc(data, streamLen);
+ LOG_ALWAYS_FATAL_IF(!data);
+ }
+ // Just in case sk_free differs from free, we ask Skia to use
+ // free to cleanup the buffer that SkData wraps.
+ return SkData::MakeWithProc(data, streamLen, free_pointer_skproc, nullptr);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index 69774cc6e133..0c3af61fc089 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include "FontUtils.h"
@@ -89,7 +86,8 @@ static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) {
}
std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
builder->langId, builder->variant, std::move(builder->fonts),
- true /* isCustomFallback */, false /* isDefaultFallback */);
+ true /* isCustomFallback */, false /* isDefaultFallback */,
+ minikin::VariationFamilyType::None);
if (family->getCoverage().length() == 0) {
return 0;
}
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 78b4f7b7654d..7cc48661619a 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -1,6 +1,3 @@
-#undef LOG_TAG
-#define LOG_TAG "GraphicsJNI"
-
#include <assert.h>
#include <unistd.h>
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 23ab5dd38b1a..b9fff36d372e 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -125,14 +125,6 @@ public:
static jobject createBitmapRegionDecoder(JNIEnv* env,
android::BitmapRegionDecoderWrapper* bitmap);
- /**
- * Given a bitmap we natively allocate a memory block to store the contents
- * of that bitmap. The memory is then attached to the bitmap via an
- * SkPixelRef, which ensures that upon deletion the appropriate caches
- * are notified.
- */
- static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap);
-
/** Copy the colors in colors[] to the bitmap, convert to the correct
format along the way.
Whether to use premultiplied pixels is determined by dstBitmap's alphaType.
diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp
index e32c9115483c..54369b9e4384 100644
--- a/libs/hwui/jni/GraphicsStatsService.cpp
+++ b/libs/hwui/jni/GraphicsStatsService.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "GraphicsStatsService"
-
#include <JankTracker.h>
#include <log/log.h>
#include <nativehelper/ScopedPrimitiveArray.h>
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 048ce025ce27..cbd452031f69 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -1,6 +1,5 @@
#include "GraphicsJNI.h"
#include "SkMaskFilter.h"
-#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
#include "SkBlurTypes.h"
#include "SkTableMaskFilter.h"
@@ -11,6 +10,13 @@ static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
}
}
+// From https://skia.googlesource.com/skia/+/d74c99a3cd5eef5f16b2eb226e6b45fe523c8552/src/core/SkBlurMask.cpp#28
+static constexpr float kBLUR_SIGMA_SCALE = 0.57735f;
+
+static float convertRadiusToSigma(float radius) {
+ return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+}
+
class SkMaskFilterGlue {
public:
static void destructor(JNIEnv* env, jobject, jlong filterHandle) {
@@ -19,7 +25,7 @@ public:
}
static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
- SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+ SkScalar sigma = convertRadiusToSigma(radius);
SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release();
ThrowIAE_IfNull(env, filter);
return reinterpret_cast<jlong>(filter);
@@ -34,7 +40,7 @@ public:
direction[i] = values[i];
}
- SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+ SkScalar sigma = convertRadiusToSigma(radius);
SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma,
direction, ambient, specular).release();
ThrowIAE_IfNull(env, filter);
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index d50a8a22b5cb..67ef143e6179 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -15,8 +15,6 @@
** limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "9patch"
#define LOG_NDEBUG 1
#include <androidfw/ResourceTypes.h>
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 13357fa25e8c..7aef7a51b90c 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -15,16 +15,29 @@
** limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "Paint"
-
-#include <utils/Log.h>
-
-#include "GraphicsJNI.h"
+#include <hwui/BlurDrawLooper.h>
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
+#include <minikin/GraphemeBreak.h>
+#include <minikin/LocaleList.h>
+#include <minikin/Measurement.h>
+#include <minikin/MinikinPaint.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedStringChars.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
+#include <unicode/utf16.h>
+#include <utils/Log.h>
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+#include "ColorFilter.h"
+#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkColorFilter.h"
#include "SkColorSpace.h"
#include "SkFont.h"
@@ -35,27 +48,21 @@
#include "SkPathEffect.h"
#include "SkPathUtils.h"
#include "SkShader.h"
-#include "SkBlendMode.h"
#include "unicode/uloc.h"
#include "utils/Blur.h"
-#include <hwui/BlurDrawLooper.h>
-#include <hwui/MinikinSkia.h>
-#include <hwui/MinikinUtils.h>
-#include <hwui/Paint.h>
-#include <hwui/Typeface.h>
-#include <minikin/GraphemeBreak.h>
-#include <minikin/LocaleList.h>
-#include <minikin/Measurement.h>
-#include <minikin/MinikinPaint.h>
-#include <unicode/utf16.h>
+namespace android {
-#include <cassert>
-#include <cstring>
-#include <memory>
-#include <vector>
+namespace {
-namespace android {
+void copyMinikinRectToSkRect(const minikin::MinikinRect& minikinRect, SkRect* skRect) {
+ skRect->fLeft = minikinRect.mLeft;
+ skRect->fTop = minikinRect.mTop;
+ skRect->fRight = minikinRect.mRight;
+ skRect->fBottom = minikinRect.mBottom;
+}
+
+} // namespace
static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
const SkPoint pos[], SkPath* dst) {
@@ -105,8 +112,8 @@ namespace PaintGlue {
float measured = 0;
std::unique_ptr<float[]> advancesArray(new float[count]);
- MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text,
- 0, count, count, advancesArray.get());
+ MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0,
+ count, count, advancesArray.get(), nullptr);
for (int i = 0; i < count; i++) {
// traverse in the given direction
@@ -196,9 +203,9 @@ namespace PaintGlue {
if (advances) {
advancesArray.reset(new jfloat[count]);
}
- const float advance = MinikinUtils::measureText(paint,
- static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount,
- advancesArray.get());
+ const float advance = MinikinUtils::measureText(
+ paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count,
+ contextCount, advancesArray.get(), nullptr);
if (advances) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
@@ -236,7 +243,7 @@ namespace PaintGlue {
minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
- advancesArray.get());
+ advancesArray.get(), nullptr);
size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
start, count, offset, moveOpt);
return static_cast<jint>(result);
@@ -500,7 +507,7 @@ namespace PaintGlue {
static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface,
const jchar buf[], jint start, jint count, jint bufSize,
jboolean isRtl, jint offset, jfloatArray advances,
- jint advancesIndex) {
+ jint advancesIndex, SkRect* drawBounds) {
if (advances) {
size_t advancesLength = env->GetArrayLength(advances);
if ((size_t)(count + advancesIndex) > advancesLength) {
@@ -509,14 +516,23 @@ namespace PaintGlue {
}
}
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ minikin::MinikinRect bounds;
if (offset == start + count && advances == nullptr) {
- return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
- bufSize, nullptr);
+ float result =
+ MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
+ bufSize, nullptr, drawBounds ? &bounds : nullptr);
+ if (drawBounds) {
+ copyMinikinRectToSkRect(bounds, drawBounds);
+ }
+ return result;
}
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
- advancesArray.get());
+ advancesArray.get(), drawBounds ? &bounds : nullptr);
+ if (drawBounds) {
+ copyMinikinRectToSkRect(bounds, drawBounds);
+ }
float result = minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
if (advances) {
minikin::distributeAdvances(advancesArray.get(), buf, start, count);
@@ -532,7 +548,7 @@ namespace PaintGlue {
ScopedCharArrayRO textArray(env, text);
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
- isRtl, offset - contextStart, nullptr, 0);
+ isRtl, offset - contextStart, nullptr, 0, nullptr);
return result;
}
@@ -540,13 +556,19 @@ namespace PaintGlue {
jcharArray text, jint start, jint end,
jint contextStart, jint contextEnd,
jboolean isRtl, jint offset,
- jfloatArray advances, jint advancesIndex) {
+ jfloatArray advances, jint advancesIndex,
+ jobject drawBounds) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
ScopedCharArrayRO textArray(env, text);
+ SkRect skDrawBounds;
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
- isRtl, offset - contextStart, advances, advancesIndex);
+ isRtl, offset - contextStart, advances, advancesIndex,
+ drawBounds ? &skDrawBounds : nullptr);
+ if (drawBounds != nullptr) {
+ GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds);
+ }
return result;
}
@@ -555,7 +577,7 @@ namespace PaintGlue {
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
- advancesArray.get());
+ advancesArray.get(), nullptr);
return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
}
@@ -584,7 +606,7 @@ namespace PaintGlue {
minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
float saveSkewX = font->getSkewX();
bool savefakeBold = font->isEmbolden();
- MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery);
+ MinikinFontSkia::populateSkFont(font, baseFont.typeface().get(), baseFont.fakery);
SkScalar spacing = font->getMetrics(metrics);
// The populateSkPaint call may have changed fake bold / text skew
// because we want to measure with those effects applied, so now
@@ -821,9 +843,11 @@ namespace PaintGlue {
static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) {
Paint* obj = reinterpret_cast<Paint *>(objHandle);
- SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
- obj->setColorFilter(sk_ref_sp(filter));
- return reinterpret_cast<jlong>(obj->getColorFilter());
+ auto colorFilter = uirenderer::ColorFilter::fromJava(filterHandle);
+ auto skColorFilter =
+ colorFilter != nullptr ? colorFilter->getInstance() : sk_sp<SkColorFilter>();
+ obj->setColorFilter(skColorFilter);
+ return filterHandle;
}
static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) {
@@ -1083,7 +1107,7 @@ static const JNINativeMethod methods[] = {
(void*)PaintGlue::getCharArrayBounds},
{"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph},
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F},
- {"nGetRunCharacterAdvance", "(J[CIIIIZI[FI)F",
+ {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F",
(void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I},
{"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index f3db1705e694..dcd3fa4932fc 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -14,13 +14,13 @@
* limitations under the License.
*/
#include "Bitmap.h"
+#include "ColorFilter.h"
#include "GraphicsJNI.h"
#include "SkBlendMode.h"
#include "SkImageFilter.h"
#include "SkImageFilters.h"
#include "graphics_jni_helpers.h"
#include "utils/Blur.h"
-#include <utils/Log.h>
using namespace android::uirenderer;
@@ -76,11 +76,13 @@ static jlong createColorFilterEffect(
jlong colorFilterHandle,
jlong inputFilterHandle
) {
- auto* colorFilter = reinterpret_cast<const SkColorFilter*>(colorFilterHandle);
+ auto colorFilter = android::uirenderer::ColorFilter::fromJava(colorFilterHandle);
+ auto skColorFilter =
+ colorFilter != nullptr ? colorFilter->getInstance() : sk_sp<SkColorFilter>();
auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle);
- sk_sp<SkImageFilter> colorFilterImageFilter = SkImageFilters::ColorFilter(
- sk_ref_sp(colorFilter), sk_ref_sp(inputFilter), nullptr);
- return reinterpret_cast<jlong>(colorFilterImageFilter.release());
+ sk_sp<SkImageFilter> colorFilterImageFilter =
+ SkImageFilters::ColorFilter(skColorFilter, sk_ref_sp(inputFilter), nullptr);
+ return reinterpret_cast<jlong>(colorFilterImageFilter.release());
}
static jlong createBlendModeEffect(
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 7eb79be6f55b..2c13ceb77b52 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -1,6 +1,3 @@
-#undef LOG_TAG
-#define LOG_TAG "ShaderJNI"
-
#include <vector>
#include "Gainmap.h"
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 69418b09fee6..4dbfa88d6301 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -1,6 +1,3 @@
-#undef LOG_TAG
-#define LOG_TAG "YuvToJpegEncoder"
-
#include "CreateJavaOutputStreamAdaptor.h"
#include "SkStream.h"
#include "YuvToJpegEncoder.h"
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index f060bb32031a..426644ee6a4e 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -84,7 +84,7 @@ static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_P
canvas->resetRecording(width, height, renderNode);
}
-static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) {
+static jint android_view_DisplayListCanvas_getMaxTextureSize(JNIEnv*, jobject) {
#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread)
return android::uirenderer::renderthread::RenderProxy::maxTextureSize();
#else
@@ -175,14 +175,14 @@ static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAM
const char* const kClassPathName = "android/graphics/RecordingCanvas";
static JNINativeMethod gMethods[] = {
+ {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize},
+ {"nGetMaximumTextureHeight", "()I",
+ (void*)android_view_DisplayListCanvas_getMaxTextureSize},
// ------------ @CriticalNative --------------
{"nCreateDisplayListCanvas", "(JII)J",
(void*)android_view_DisplayListCanvas_createDisplayListCanvas},
{"nResetDisplayListCanvas", "(JJII)V",
(void*)android_view_DisplayListCanvas_resetDisplayListCanvas},
- {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize},
- {"nGetMaximumTextureHeight", "()I",
- (void*)android_view_DisplayListCanvas_getMaxTextureSize},
{"nEnableZ", "(JZ)V", (void*)android_view_DisplayListCanvas_enableZ},
{"nFinishRecording", "(JJ)V", (void*)android_view_DisplayListCanvas_finishRecording},
{"nDrawRenderNode", "(JJ)V", (void*)android_view_DisplayListCanvas_drawRenderNode},
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
index 706f18c3be80..e3cdee6e7034 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "HardwareBufferRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
#include <GraphicsJNI.h>
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index d04de37f6961..422ffeaecfd0 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "ThreadedRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
#include <FrameInfo.h>
@@ -27,7 +25,7 @@
#include <SkColorSpace.h>
#include <SkData.h>
#include <SkImage.h>
-#include <SkImagePriv.h>
+#include <SkImageAndroid.h>
#include <SkPicture.h>
#include <SkPixmap.h>
#include <SkSerialProcs.h>
@@ -35,6 +33,7 @@
#include <SkTypeface.h>
#include <dlfcn.h>
#include <gui/TraceUtils.h>
+#include <include/encode/SkPngEncoder.h>
#include <inttypes.h>
#include <media/NdkImage.h>
#include <media/NdkImageReader.h>
@@ -54,6 +53,7 @@
#include <algorithm>
#include <atomic>
+#include <log/log.h>
#include <vector>
#include "JvmErrorReporter.h"
@@ -477,7 +477,7 @@ public:
// actually cross thread boundaries here, make a copy so it's immutable proper
if (bitmap && !bitmap->isImmutable()) {
ATRACE_NAME("Copying mutable bitmap");
- return SkImage::MakeFromBitmap(*bitmap);
+ return SkImages::RasterFromBitmap(*bitmap);
}
if (img->isTextureBacked()) {
ATRACE_NAME("Readback of texture image");
@@ -497,7 +497,7 @@ public:
return sk_ref_sp(img);
}
bm.setImmutable();
- return SkMakeImageFromRasterBitmap(bm, kNever_SkCopyPixelsMode);
+ return SkImages::PinnableRasterFromBitmap(bm);
}
return sk_ref_sp(img);
}
@@ -524,7 +524,16 @@ public:
if (iter != context->mTextureMap.end()) {
img = iter->second.get();
}
- return img->encodeToData();
+ if (!img) {
+ return nullptr;
+ }
+ // The following encode (specifically the pixel readback) will fail on a
+ // texture-backed image. They should already be raster images, but on
+ // the off-chance they aren't, we will just serialize it as nothing.
+ if (img->isTextureBacked()) {
+ return SkData::MakeEmpty();
+ }
+ return SkPngEncoder::Encode(nullptr, img, {});
}
void serialize(SkWStream* stream) const override {
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8c7b9a4b5e94..2a218a25913d 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -243,6 +243,13 @@ static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMM
return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
}
+static jboolean android_view_RenderNode_setBackdropRenderEffect(
+ CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong renderEffectPtr) {
+ SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setBackdropImageFilter, imageFilter,
+ RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -792,6 +799,8 @@ static const JNINativeMethod gMethods[] = {
{"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
{"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
+ {"nSetBackdropRenderEffect", "(JJ)Z",
+ (void*)android_view_RenderNode_setBackdropRenderEffect},
{"nSetHasOverlappingRendering", "(JZ)Z",
(void*)android_view_RenderNode_setHasOverlappingRendering},
{"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
index 764eff9a04be..b86c74fe0e47 100644
--- a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
+++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <Interpolator.h>
#include <cutils/log.h>
diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp
index c6d26f853c1d..40be9243affb 100644
--- a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp
+++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <Animator.h>
#include <Interpolator.h>
#include <RenderProperties.h>
diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
index 9cffceb308c8..ade48f26937f 100644
--- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#include "GraphicsJNI.h"
+#include <hwui/Paint.h>
+#include "ColorFilter.h"
+#include "GraphicsJNI.h"
#include "PathParser.h"
#include "VectorDrawable.h"
-#include <hwui/Paint.h>
-
namespace android {
using namespace uirenderer;
using namespace uirenderer::VectorDrawable;
@@ -108,8 +108,9 @@ static jint draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
SkRect rect;
GraphicsJNI::jrect_to_rect(env, jrect, &rect);
- SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
- return tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache);
+ auto colorFilter = ColorFilter::fromJava(colorFilterPtr);
+ auto skColorFilter = colorFilter != nullptr ? colorFilter->getInstance() : nullptr;
+ return tree->draw(canvas, skColorFilter.get(), rect, needsMirroring, canReuseCache);
}
/**
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 1af60b2f5fae..2ec94c954fe9 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
#include "Font.h"
#include "SkData.h"
#include "SkFont.h"
@@ -127,7 +124,7 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr,
jint weight, jboolean italic, jint ttcIndex) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get());
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get());
std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
// Reconstruct SkTypeface with different arguments from existing SkTypeface.
@@ -159,7 +156,7 @@ static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong
static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
jlong paintHandle, jobject rect) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get());
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get());
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
SkFont* skFont = &paint->getSkFont();
@@ -179,7 +176,7 @@ static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint g
static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle,
jobject metricsObj) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get());
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get());
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
SkFont* skFont = &paint->getSkFont();
@@ -209,7 +206,7 @@ static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
// Fast Native
static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
- const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface();
return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()),
minikinFont->GetFontSize());
}
@@ -217,7 +214,7 @@ static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
// Critical Native
static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
- return reinterpret_cast<jlong>(font->font->typeface()->GetFontData());
+ return reinterpret_cast<jlong>(font->font->baseTypeface()->GetFontData());
}
// Critical Native
@@ -236,7 +233,7 @@ static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
}
return env->NewStringUTF(path.c_str());
} else {
- const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface();
const std::string& path = minikinFont->GetFontPath();
if (path.empty()) {
return nullptr;
@@ -275,7 +272,7 @@ static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
reader.skipString(); // fontPath
return reader.read<int>();
} else {
- const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface();
return minikinFont->GetFontIndex();
}
}
@@ -289,7 +286,7 @@ static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
reader.skip<int>(); // fontIndex
return reader.readArray<minikin::FontVariation>().second;
} else {
- const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface();
return minikinFont->GetAxes().size();
}
}
@@ -304,7 +301,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde
reader.skip<int>(); // fontIndex
var = reader.readArray<minikin::FontVariation>().first[index];
} else {
- const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface();
var = minikinFont->GetAxes().at(index);
}
uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
@@ -314,7 +311,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde
// Critical Native
static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
- return font->font->typeface()->GetSourceId();
+ return font->font->baseTypeface()->GetSourceId();
}
static jlongArray Font_getAvailableFontSet(JNIEnv* env, jobject) {
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index ee158ee4e316..462c8c8f2fb0 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
#include "graphics_jni_helpers.h"
#include <nativehelper/ScopedUtfChars.h>
@@ -60,7 +57,7 @@ static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPt
// Regular JNI
static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr,
jstring langTags, jint variant, jboolean isCustomFallback,
- jboolean isDefaultFallback) {
+ jboolean isDefaultFallback, jint variationFamilyType) {
std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
uint32_t localeId;
if (langTags == nullptr) {
@@ -71,7 +68,8 @@ static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderP
}
std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
- isCustomFallback, isDefaultFallback);
+ isCustomFallback, isDefaultFallback,
+ static_cast<minikin::VariationFamilyType>(variationFamilyType));
if (family->getCoverage().length() == 0) {
// No coverage means minikin rejected given font for some reasons.
jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -121,7 +119,7 @@ static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
{"nInitBuilder", "()J", (void*)FontFamily_Builder_initBuilder},
{"nAddFont", "(JJ)V", (void*)FontFamily_Builder_addFont},
- {"nBuild", "(JLjava/lang/String;IZZ)J", (void*)FontFamily_Builder_build},
+ {"nBuild", "(JLjava/lang/String;IZZI)J", (void*)FontFamily_Builder_build},
{"nGetReleaseNativeFamily", "()J", (void*)FontFamily_Builder_GetReleaseFunc},
};
diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp
index 427bafa1bd83..3b18f5f54187 100644
--- a/libs/hwui/jni/pdf/PdfEditor.cpp
+++ b/libs/hwui/jni/pdf/PdfEditor.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "PdfEditor"
-
#include <sys/types.h>
#include <unistd.h>
diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp
index 06d202828b85..6887fdacd68f 100644
--- a/libs/hwui/jni/pdf/PdfUtils.cpp
+++ b/libs/hwui/jni/pdf/PdfUtils.cpp
@@ -16,14 +16,11 @@
#include "PdfUtils.h"
-#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
#include "fpdfview.h"
-
-#undef LOG_TAG
-#define LOG_TAG "PdfUtils"
-#include <utils/Log.h>
+#include "jni.h"
namespace android {
diff --git a/libs/hwui/jni/text/GraphemeBreak.cpp b/libs/hwui/jni/text/GraphemeBreak.cpp
index 55f03bd9f7b1..322af7e9f3ee 100644
--- a/libs/hwui/jni/text/GraphemeBreak.cpp
+++ b/libs/hwui/jni/text/GraphemeBreak.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "GraphemeBreaker"
-
#include <minikin/GraphemeBreak.h>
#include <nativehelper/ScopedPrimitiveArray.h>
diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp
index 69865171a09d..c512256ed9b9 100644
--- a/libs/hwui/jni/text/LineBreaker.cpp
+++ b/libs/hwui/jni/text/LineBreaker.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "LineBreaker"
-
#include "utils/misc.h"
#include "utils/Log.h"
#include "graphics_jni_helpers.h"
@@ -54,13 +51,12 @@ static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
// hyphenFrequency)
-static jlong nInit(JNIEnv* env, jclass /* unused */,
- jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) {
+static jlong nInit(JNIEnv* env, jclass /* unused */, jint breakStrategy, jint hyphenationFrequency,
+ jboolean isJustified, jintArray indents, jboolean useBoundsForWidth) {
return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
static_cast<minikin::BreakStrategy>(breakStrategy),
- static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
- isJustified,
- jintArrayToFloatVector(env, indents)));
+ static_cast<minikin::HyphenationFrequency>(hyphenationFrequency), isJustified,
+ jintArrayToFloatVector(env, indents), useBoundsForWidth));
}
static void nFinish(jlong nativePtr) {
@@ -131,39 +127,44 @@ static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) {
}
static const JNINativeMethod gMethods[] = {
- // Fast Natives
- {"nInit", "("
- "I" // breakStrategy
- "I" // hyphenationFrequency
- "Z" // isJustified
- "[I" // indents
- ")J", (void*) nInit},
-
- // Critical Natives
- {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
-
- // Regular JNI
- {"nComputeLineBreaks", "("
- "J" // nativePtr
- "[C" // text
- "J" // MeasuredParagraph ptr.
- "I" // length
- "F" // firstWidth
- "I" // firstWidthLineCount
- "F" // restWidth
- "[F" // variableTabStops
- "F" // defaultTabStop
- "I" // indentsOffset
- ")J", (void*) nComputeLineBreaks},
-
- // Result accessors, CriticalNatives
- {"nGetLineCount", "(J)I", (void*)nGetLineCount},
- {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
- {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
- {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
- {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
- {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
- {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
+ // Fast Natives
+ {"nInit",
+ "("
+ "I" // breakStrategy
+ "I" // hyphenationFrequency
+ "Z" // isJustified
+ "[I" // indents
+ "Z" // useBoundsForWidth
+ ")J",
+ (void*)nInit},
+
+ // Critical Natives
+ {"nGetReleaseFunc", "()J", (void*)nGetReleaseFunc},
+
+ // Regular JNI
+ {"nComputeLineBreaks",
+ "("
+ "J" // nativePtr
+ "[C" // text
+ "J" // MeasuredParagraph ptr.
+ "I" // length
+ "F" // firstWidth
+ "I" // firstWidthLineCount
+ "F" // restWidth
+ "[F" // variableTabStops
+ "F" // defaultTabStop
+ "I" // indentsOffset
+ ")J",
+ (void*)nComputeLineBreaks},
+
+ // Result accessors, CriticalNatives
+ {"nGetLineCount", "(J)I", (void*)nGetLineCount},
+ {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
+ {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
+ {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
+ {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
+ {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
+ {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
};
int register_android_graphics_text_LineBreaker(JNIEnv* env) {
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index c13c800651ef..746745afbf09 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "MeasuredText"
-
#include "GraphicsJNI.h"
#include "utils/misc.h"
#include "utils/Log.h"
@@ -65,13 +62,14 @@ static jlong nInitBuilder(CRITICAL_JNI_PARAMS) {
// Regular JNI
static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
- jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end,
- jboolean isRtl) {
+ jlong paintPtr, jint lbStyle, jint lbWordStyle, jboolean hyphenation,
+ jint start, jint end, jboolean isRtl) {
Paint* paint = toPaint(paintPtr);
const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
toBuilder(builderPtr)
- ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl);
+ ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, hyphenation,
+ isRtl);
}
// Regular JNI
@@ -84,13 +82,14 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong
// Regular JNI
static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, jlong hintPtr,
jcharArray javaText, jboolean computeHyphenation,
- jboolean computeLayout, jboolean fastHyphenationMode) {
+ jboolean computeLayout, jboolean computeBounds,
+ jboolean fastHyphenationMode) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
return toJLong(toBuilder(builderPtr)
- ->build(textBuffer, computeHyphenation, computeLayout,
+ ->build(textBuffer, computeHyphenation, computeLayout, computeBounds,
fastHyphenationMode, toMeasuredParagraph(hintPtr))
.release());
}
@@ -161,9 +160,9 @@ static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
static const JNINativeMethod gMTBuilderMethods[] = {
// MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*)nInitBuilder},
- {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun},
+ {"nAddStyleRun", "(JJIIZIIZ)V", (void*)nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun},
- {"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText},
+ {"nBuildMeasuredText", "(JJ[CZZZZ)J", (void*)nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*)nFreeBuilder},
};
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 8e4dd53069f4..6c05346d26da 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#undef LOG_TAG
-#define LOG_TAG "TextShaper"
-
#include "graphics_jni_helpers.h"
#include <nativehelper/ScopedStringChars.h>
#include <nativehelper/ScopedPrimitiveArray.h>
@@ -62,7 +59,7 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou
const minikin::Font* font = layout.getFont(i);
if (seenFonts.find(font) != seenFonts.end()) continue;
minikin::MinikinExtent extent = {};
- font->typeface()->GetFontExtent(&extent, minikinPaint, layout.getFakery(i));
+ layout.typeface(i)->GetFontExtent(&extent, minikinPaint, layout.getFakery(i));
overallAscent = std::min(overallAscent, extent.ascent);
overallDescent = std::max(overallDescent, extent.descent);
}
@@ -148,6 +145,30 @@ static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i
}
// CriticalNative
+static jboolean TextShaper_Result_getFakeBold(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).isFakeBold();
+}
+
+// CriticalNative
+static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).isFakeItalic();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).wghtAdjustment();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).italAdjustment();
+}
+
+// CriticalNative
static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
@@ -185,15 +206,19 @@ static const JNINativeMethod gMethods[] = {
};
static const JNINativeMethod gResultMethods[] = {
- { "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount },
- { "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance },
- { "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent },
- { "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent },
- { "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId },
- { "nGetX", "(JI)F", (void*)TextShaper_Result_getX },
- { "nGetY", "(JI)F", (void*)TextShaper_Result_getY },
- { "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont },
- { "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc },
+ {"nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount},
+ {"nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance},
+ {"nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent},
+ {"nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent},
+ {"nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId},
+ {"nGetX", "(JI)F", (void*)TextShaper_Result_getX},
+ {"nGetY", "(JI)F", (void*)TextShaper_Result_getY},
+ {"nGetFont", "(JI)J", (void*)TextShaper_Result_getFont},
+ {"nGetFakeBold", "(JI)Z", (void*)TextShaper_Result_getFakeBold},
+ {"nGetFakeItalic", "(JI)Z", (void*)TextShaper_Result_getFakeItalic},
+ {"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride},
+ {"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride},
+ {"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc},
};
int register_android_graphics_text_TextShaper(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
new file mode 100644
index 000000000000..e81cbfb508ae
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#include "BackdropFilterDrawable.h"
+
+#include <SkImage.h>
+#include <SkSurface.h>
+
+#include "RenderNode.h"
+#include "RenderNodeDrawable.h"
+#ifdef __ANDROID__
+#include "include/gpu/ganesh/SkImageGanesh.h"
+#endif
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+BackdropFilterDrawable::~BackdropFilterDrawable() {}
+
+bool BackdropFilterDrawable::prepareToDraw(SkCanvas* canvas, const RenderProperties& properties,
+ int backdropImageWidth, int backdropImageHeight) {
+ // the drawing bounds for blurred content.
+ mDstBounds.setWH(properties.getWidth(), properties.getHeight());
+
+ float alphaMultiplier = 1.0f;
+ RenderNodeDrawable::setViewProperties(properties, canvas, &alphaMultiplier, true);
+
+ // get proper subset for previous content.
+ canvas->getTotalMatrix().mapRect(&mImageSubset, mDstBounds);
+ SkRect imageSubset(mImageSubset);
+ // ensure the subset is inside bounds of previous content.
+ if (!mImageSubset.intersect(SkRect::MakeWH(backdropImageWidth, backdropImageHeight))) {
+ return false;
+ }
+
+ // correct the drawing bounds if subset was changed.
+ if (mImageSubset != imageSubset) {
+ SkMatrix inverse;
+ if (canvas->getTotalMatrix().invert(&inverse)) {
+ inverse.mapRect(&mDstBounds, mImageSubset);
+ }
+ }
+
+ // follow the alpha from the target RenderNode.
+ mPaint.setAlpha(properties.layerProperties().alpha() * alphaMultiplier);
+ return true;
+}
+
+void BackdropFilterDrawable::onDraw(SkCanvas* canvas) {
+ const RenderProperties& properties = mTargetRenderNode->properties();
+ auto* backdropFilter = properties.layerProperties().getBackdropImageFilter();
+ auto* surface = canvas->getSurface();
+ if (!backdropFilter || !surface) {
+ return;
+ }
+
+ auto backdropImage = surface->makeImageSnapshot();
+ // sync necessary properties from target RenderNode.
+ if (!prepareToDraw(canvas, properties, backdropImage->width(), backdropImage->height())) {
+ return;
+ }
+
+ auto imageSubset = mImageSubset.roundOut();
+#ifdef __ANDROID__
+ if (canvas->recordingContext()) {
+ backdropImage =
+ SkImages::MakeWithFilter(canvas->recordingContext(), backdropImage, backdropFilter,
+ imageSubset, imageSubset, &mOutSubset, &mOutOffset);
+ } else
+#endif
+ {
+ backdropImage = SkImages::MakeWithFilter(backdropImage, backdropFilter, imageSubset,
+ imageSubset, &mOutSubset, &mOutOffset);
+ }
+ canvas->drawImageRect(backdropImage, SkRect::Make(mOutSubset), mDstBounds,
+ SkSamplingOptions(SkFilterMode::kLinear), &mPaint,
+ SkCanvas::kStrict_SrcRectConstraint);
+}
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.h b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
new file mode 100644
index 000000000000..9e35837675ae
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable captures it's backdrop content and render it with a
+ * image filter.
+ */
+class BackdropFilterDrawable : public SkDrawable {
+public:
+ BackdropFilterDrawable(RenderNode* renderNode, SkCanvas* canvas)
+ : mTargetRenderNode(renderNode), mBounds(canvas->getLocalClipBounds()) {}
+
+ ~BackdropFilterDrawable();
+
+private:
+ RenderNode* mTargetRenderNode;
+ SkPaint mPaint;
+
+ SkRect mDstBounds;
+ SkRect mImageSubset;
+ SkIRect mOutSubset;
+ SkIPoint mOutOffset;
+
+ /**
+ * Check all necessary properties before actual drawing.
+ * Return true if ready to draw.
+ */
+ bool prepareToDraw(SkCanvas* canvas, const RenderProperties& properties, int backdropImageWidth,
+ int backdropImageHeight);
+
+protected:
+ void onDraw(SkCanvas* canvas) override;
+
+ virtual SkRect onGetBounds() override { return mBounds; }
+ const SkRect mBounds;
+};
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 8d5967bbd461..5d3fb30769ed 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -21,10 +21,15 @@
#include "GrBackendSurface.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
+#include "SkCanvas.h"
+#include "SkCanvasAndroid.h"
#include "SkClipStack.h"
#include "SkRect.h"
#include "SkM44.h"
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include "include/gpu/GpuTypes.h" // from Skia
+#include <include/gpu/gl/GrGLTypes.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
#include "utils/GLUtils.h"
#include <effects/GainmapRenderer.h>
#include "renderthread/CanvasContext.h"
@@ -34,7 +39,7 @@ namespace uirenderer {
namespace skiapipeline {
static void setScissor(int viewportHeight, const SkIRect& clip) {
- SkASSERT(!clip.isEmpty());
+ LOG_FATAL_IF(clip.isEmpty(), "empty scissor clip");
// transform to Y-flipped GL space, and prevent negatives
GLint y = viewportHeight - clip.fBottom;
GLint height = (viewportHeight - clip.fTop) - y;
@@ -42,9 +47,9 @@ static void setScissor(int viewportHeight, const SkIRect& clip) {
}
static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
- GrBackendRenderTarget renderTarget = canvas->topLayerBackendRenderTarget();
+ GrBackendRenderTarget renderTarget = skgpu::ganesh::TopLayerBackendRenderTarget(canvas);
GrGLFramebufferInfo fboInfo;
- LOG_ALWAYS_FATAL_IF(!renderTarget.getGLFramebufferInfo(&fboInfo),
+ LOG_ALWAYS_FATAL_IF(!GrBackendRenderTargets::GetGLFramebufferInfo(renderTarget, &fboInfo),
"getGLFrameBufferInfo failed");
*outFboID = fboInfo.fFBOID;
@@ -76,13 +81,13 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
}
// flush will create a GrRenderTarget if not already present.
- canvas->flush();
+ directContext->flushAndSubmit();
GLuint fboID = 0;
SkISize fboSize;
GetFboDetails(canvas, &fboID, &fboSize);
- SkIRect surfaceBounds = canvas->topLayerBounds();
+ SkIRect surfaceBounds = skgpu::ganesh::TopLayerBounds(canvas);
SkIRect clipBounds = canvas->getDeviceClipBounds();
SkM44 mat4(canvas->getLocalToDevice());
SkRegion clipRegion;
@@ -95,12 +100,14 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
SkImageInfo surfaceInfo =
canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
tmpSurface =
- SkSurface::MakeRenderTarget(directContext, skgpu::Budgeted::kYes, surfaceInfo);
+ SkSurfaces::RenderTarget(directContext, skgpu::Budgeted::kYes, surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
GrGLFramebufferInfo fboInfo;
- if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
- .getGLFramebufferInfo(&fboInfo)) {
+ if (!GrBackendRenderTargets::GetGLFramebufferInfo(
+ SkSurfaces::GetBackendRenderTarget(
+ tmpSurface.get(), SkSurfaces::BackendHandleAccess::kFlushWrite),
+ &fboInfo)) {
ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
return;
}
@@ -163,7 +170,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
// GL ops get inserted here if previous flush is missing, which could dirty the stencil
bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
- tmpCanvas->flush(); // need this flush for the single op that draws into the stencil
+ directContext->flushAndSubmit(); // need this flush for the single op that draws into the stencil
// ensure that the framebuffer that the webview will render into is bound before after we
// draw into the stencil
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index da4f66d45a70..2b2e3995d17e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -15,21 +15,25 @@
*/
#include "RenderNodeDrawable.h"
+
#include <SkPaint.h>
#include <SkPaintFilterCanvas.h>
#include <SkPoint.h>
#include <SkRRect.h>
#include <SkRect.h>
#include <gui/TraceUtils.h>
+#include <include/effects/SkImageFilters.h>
+#ifdef __ANDROID__
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#endif
+
+#include <optional>
+
#include "RenderNode.h"
#include "SkiaDisplayList.h"
#include "StretchMask.h"
#include "TransformCanvas.h"
-#include <include/effects/SkImageFilters.h>
-
-#include <optional>
-
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -255,9 +259,19 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
if (imageFilter) {
auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
- snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter,
- subset, clipBounds.roundOut(),
- &srcBounds, &offset);
+
+#ifdef __ANDROID__
+ if (recordingContext) {
+ snapshotImage = SkImages::MakeWithFilter(
+ recordingContext, snapshotImage, imageFilter, subset,
+ clipBounds.roundOut(), &srcBounds, &offset);
+ } else
+#endif
+ {
+ snapshotImage = SkImages::MakeWithFilter(snapshotImage, imageFilter, subset,
+ clipBounds.roundOut(), &srcBounds,
+ &offset);
+ }
}
} else {
const auto snapshotResult = renderNode->updateSnapshotIfRequired(
@@ -362,7 +376,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
}
void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier) {
+ float* alphaMultiplier, bool ignoreLayer) {
if (properties.getLeft() != 0 || properties.getTop() != 0) {
canvas->translate(properties.getLeft(), properties.getTop());
}
@@ -378,7 +392,8 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S
canvas->concat(*properties.getTransformMatrix());
}
}
- if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
+ if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale &&
+ !ignoreLayer) {
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
canvas->concat(
@@ -388,10 +403,10 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S
const bool isLayer = properties.effectiveLayerType() != LayerType::None;
int clipFlags = properties.getClippingFlags();
if (properties.getAlpha() < 1) {
- if (isLayer) {
+ if (isLayer && !ignoreLayer) {
clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
}
- if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+ if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering()) || ignoreLayer) {
*alphaMultiplier = properties.getAlpha();
} else {
// savelayer needed to create an offscreen buffer
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index c7582e734009..818ac45bf346 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -120,7 +120,7 @@ private:
* Applies the rendering properties of a view onto a SkCanvas.
*/
static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier);
+ float* alphaMultiplier, bool ignoreLayer = false);
/**
* Stores transform on the canvas at time of recording and is used for
@@ -149,6 +149,11 @@ private:
* display list that is searched for any render nodes with getProjectBackwards==true
*/
SkiaDisplayList* mProjectedDisplayList = nullptr;
+
+ /**
+ * Allow BackdropFilterDrawable to apply same render properties onto SkCanvas.
+ */
+ friend class BackdropFilterDrawable;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 11977bd54c2c..136740c799dd 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -27,7 +27,6 @@
#include <SkRect.h>
#include <SkScalar.h>
#include <SkShadowUtils.h>
-#include <include/private/SkShadowFlags.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 74955503dbb1..6ccb212fe6ca 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -26,6 +26,7 @@
#include <string>
#include <vector>
+class GrDirectContext;
class SkData;
namespace android {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index af2d3b34bac7..5c8285a8e1e9 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -66,6 +66,12 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn)
}
}
+void SkiaDisplayList::visit(std::function<void(const RenderNode&)> func) const {
+ for (auto& child : mChildNodes) {
+ child.getRenderNode()->visit(func);
+ }
+}
+
static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) {
Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0},
Vector3 {bounds.fRight, bounds.fTop, 0},
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 7af31a4dc4c6..e5bd5c9b2a3b 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -145,6 +145,8 @@ public:
*/
void updateChildren(std::function<void(RenderNode*)> updateFn);
+ void visit(std::function<void(const RenderNode&)> func) const;
+
/**
* Returns true if there is a child render node that is a projection receiver.
*/
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index cf31173d266e..774478669058 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -16,6 +16,9 @@
#include "SkiaOpenGLPipeline.h"
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/gl/GrGLTypes.h>
#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
@@ -138,7 +141,8 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
LOG_ALWAYS_FATAL("Unsupported color type.");
}
- GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
+ auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0,
+ STENCIL_BUFFER_SIZE, fboInfo);
SkSurfaceProps props(mColorMode == ColorMode::Default ? 0 : SkSurfaceProps::kAlwaysDither_Flag,
kUnknown_SkPixelGeometry);
@@ -150,9 +154,9 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
surface = getBufferSkSurface(bufferParams);
preTransform = bufferParams.getTransform();
} else {
- surface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT,
- getSurfaceOrigin(), colorType,
- mSurfaceColorSpace, &props);
+ surface = SkSurfaces::WrapBackendRenderTarget(mRenderThread.getGrContext(), backendRT,
+ getSurfaceOrigin(), colorType,
+ mSurfaceColorSpace, &props);
preTransform = SkMatrix::I();
}
@@ -174,7 +178,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
{
ATRACE_NAME("flush commands");
- surface->flushAndSubmit();
+ skgpu::ganesh::FlushAndSubmit(surface);
}
layerUpdateQueue->clear();
@@ -245,8 +249,7 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
- const bool isPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
- ALOGE_IF(preserveBuffer != isPreserved, "Unable to match the desired swap behavior.");
+ mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
return true;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index cb23bcc166c8..e0f1f6ef44be 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -16,14 +16,16 @@
#include "SkiaPipeline.h"
+#include <include/android/SkSurfaceAndroid.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/encode/SkPngEncoder.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkColorSpace.h>
#include <SkData.h>
#include <SkImage.h>
-#include <SkImageEncoder.h>
+#include <SkImageAndroid.h>
#include <SkImageInfo.h>
-#include <SkImagePriv.h>
#include <SkMatrix.h>
#include <SkMultiPictureDocument.h>
#include <SkOverdrawCanvas.h>
@@ -75,7 +77,7 @@ bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
return false;
}
for (SkImage* image : mutableImages) {
- if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
+ if (skgpu::ganesh::PinAsTexture(mRenderThread.getGrContext(), image)) {
mPinnedImages.emplace_back(sk_ref_sp(image));
} else {
return false;
@@ -86,7 +88,7 @@ bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
void SkiaPipeline::unpinImages() {
for (auto& image : mPinnedImages) {
- SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
+ skgpu::ganesh::UnpinTexture(mRenderThread.getGrContext(), image.get());
}
mPinnedImages.clear();
}
@@ -187,9 +189,9 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
kPremul_SkAlphaType, getSurfaceColorSpace());
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
- node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- skgpu::Budgeted::kYes, info, 0,
- this->getSurfaceOrigin(), &props));
+ node->setLayerSurface(SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
+ skgpu::Budgeted::kYes, info, 0,
+ this->getSurfaceOrigin(), &props));
if (node->getLayerSurface()) {
// update the transform in window of the layer to reset its origin wrt light source
// position
@@ -222,8 +224,8 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
auto image = bitmap->makeImage();
if (image.get()) {
- SkImage_pinAsTexture(image.get(), context);
- SkImage_unpinAsTexture(image.get(), context);
+ skgpu::ganesh::PinAsTexture(context, image.get());
+ skgpu::ganesh::UnpinTexture(context, image.get());
// A submit is necessary as there may not be a frame coming soon, so without a call
// to submit these texture uploads can just sit in the queue building up until
// we run out of RAM
@@ -439,6 +441,13 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
};
+ procs.fImageProc = [](SkImage* img, void* ctx) -> sk_sp<SkData> {
+ GrDirectContext* dCtx = static_cast<GrDirectContext*>(ctx);
+ return SkPngEncoder::Encode(dCtx,
+ img,
+ SkPngEncoder::Options{});
+ };
+ procs.fImageCtx = mRenderThread.getGrContext();
auto data = picture->serialize(&procs);
savePictureAsync(data, mCapturedFile);
mCaptureSequence = 0;
@@ -621,7 +630,7 @@ sk_sp<SkSurface> SkiaPipeline::getBufferSkSurface(
auto bufferColorSpace = bufferParams.getColorSpace();
if (mBufferSurface == nullptr || mBufferColorSpace == nullptr ||
!SkColorSpace::Equals(mBufferColorSpace.get(), bufferColorSpace.get())) {
- mBufferSurface = SkSurface::MakeFromAHardwareBuffer(
+ mBufferSurface = SkSurfaces::WrapAndroidHardwareBuffer(
mRenderThread.getGrContext(), mHardwareBuffer, kTopLeft_GrSurfaceOrigin,
bufferColorSpace, nullptr, true);
mBufferColorSpace = bufferColorSpace;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3ca7eeb37a89..e917f9a66917 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -37,6 +37,7 @@
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/VkFunctorDrawable.h"
@@ -168,6 +169,14 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
// Put Vulkan WebViews with non-rectangular clips in a HW layer
renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
}
+
+ // draw backdrop filter drawable if needed.
+ if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) {
+ auto* backdropFilterDrawable =
+ mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas());
+ drawDrawable(backdropFilterDrawable);
+ }
+
drawDrawable(&renderNodeDrawable);
// use staging property, since recording on UI thread
@@ -227,6 +236,17 @@ void SkiaRecordingCanvas::handleMutableImages(Bitmap& bitmap, DrawImagePayload&
}
}
+void SkiaRecordingCanvas::onFilterPaint(android::Paint& paint) {
+ INHERITED::onFilterPaint(paint);
+ SkShader* shader = paint.getShader();
+ // TODO(b/264559422): This only works for very specifically a BitmapShader.
+ // It's better than nothing, though
+ SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr;
+ if (image) {
+ mDisplayList->mMutableImages.push_back(image);
+ }
+}
+
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto payload = DrawImagePayload(bitmap);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index a8e4580dc200..3bd091df1ece 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -105,6 +105,8 @@ private:
void handleMutableImages(Bitmap& bitmap, DrawImagePayload& payload);
+ void onFilterPaint(Paint& paint) override;
+
using INHERITED = SkiaCanvas;
};
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index cad3703d8d2b..1676787ef671 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -18,14 +18,13 @@
#include "SkBlendMode.h"
#include "SkCanvas.h"
#include "SkSurface.h"
-#include "include/gpu/GpuTypes.h" // from Skia
#include "TransformCanvas.h"
#include "SkiaDisplayList.h"
using android::uirenderer::StretchMask;
-void StretchMask::draw(GrRecordingContext* context,
+void StretchMask::draw(GrRecordingContext*,
const StretchEffect& stretch,
const SkRect& bounds,
skiapipeline::SkiaDisplayList* displayList,
@@ -35,16 +34,14 @@ void StretchMask::draw(GrRecordingContext* context,
if (mMaskSurface == nullptr || mMaskSurface->width() != width ||
mMaskSurface->height() != height) {
// Create a new surface if we don't have one or our existing size does
- // not match.
- mMaskSurface = SkSurface::MakeRenderTarget(
- context,
- skgpu::Budgeted::kYes,
- SkImageInfo::Make(
- width,
- height,
- SkColorType::kAlpha_8_SkColorType,
- SkAlphaType::kPremul_SkAlphaType)
- );
+ // not match. SkCanvas::makeSurface returns a new surface that will
+ // be GPU-backed if canvas was also.
+ mMaskSurface = canvas->makeSurface(SkImageInfo::Make(
+ width,
+ height,
+ SkColorType::kAlpha_8_SkColorType,
+ SkAlphaType::kPremul_SkAlphaType
+ ));
mIsDirty = true;
}
@@ -53,7 +50,7 @@ void StretchMask::draw(GrRecordingContext* context,
// Make sure to apply target transformation to the mask canvas
// to ensure the replayed drawing commands generate the same result
auto previousMatrix = displayList->mParentMatrix;
- displayList->mParentMatrix = maskCanvas->getTotalMatrix();
+ displayList->mParentMatrix = maskCanvas->getLocalToDeviceAs3x3();
maskCanvas->save();
maskCanvas->drawColor(0, SkBlendMode::kClear);
TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index adf3c06b8624..475b110604e4 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -35,6 +35,8 @@
#include "effects/GainmapRenderer.h"
#include <SkBlendMode.h>
+#include <SkImage.h>
+#include <SkImageAndroid.h>
namespace android {
namespace uirenderer {
@@ -183,9 +185,9 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
// drawing into the offscreen surface, so we need to reset it here.
canvas->resetMatrix();
- auto functorImage = SkImage::MakeFromAHardwareBuffer(mFrameBuffer.get(), kPremul_SkAlphaType,
- canvas->imageInfo().refColorSpace(),
- kBottomLeft_GrSurfaceOrigin);
+ auto functorImage = SkImages::DeferredFromAHardwareBuffer(
+ mFrameBuffer.get(), kPremul_SkAlphaType, canvas->imageInfo().refColorSpace(),
+ kBottomLeft_GrSurfaceOrigin);
canvas->drawImage(functorImage, 0, 0, SkSamplingOptions(), &paint);
canvas->restore();
}
diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
index eb1f9304a5c8..ed3fabc61708 100644
--- a/libs/hwui/private/hwui/DrawGlInfo.h
+++ b/libs/hwui/private/hwui/DrawGlInfo.h
@@ -24,8 +24,7 @@ namespace android {
namespace uirenderer {
/**
- * Structure used by OpenGLRenderer::callDrawGLFunction() to pass and
- * receive data from OpenGL functors.
+ * Structure used to pass and receive data from OpenGL functors.
*/
struct DrawGlInfo {
// Input: current clip rect
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index babce88b8e1e..30d461271c89 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -17,6 +17,7 @@
#include "CacheManager.h"
#include <GrContextOptions.h>
+#include <GrTypes.h>
#include <SkExecutor.h>
#include <SkGraphics.h>
#include <math.h>
@@ -110,13 +111,18 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
contextOptions->fPersistentCache = &cache;
}
+static GrPurgeResourceOptions toSkiaEnum(bool scratchOnly) {
+ return scratchOnly ? GrPurgeResourceOptions::kScratchResourcesOnly :
+ GrPurgeResourceOptions::kAllResources;
+}
+
void CacheManager::trimMemory(TrimLevel mode) {
if (!mGrContext) {
return;
}
// flush and submit all work to the gpu and wait for it to finish
- mGrContext->flushAndSubmit(/*syncCpu=*/true);
+ mGrContext->flushAndSubmit(GrSyncCpu::kYes);
switch (mode) {
case TrimLevel::BACKGROUND:
@@ -130,7 +136,7 @@ void CacheManager::trimMemory(TrimLevel mode) {
// that have persistent data to be purged in LRU order.
mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
- mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
+ mGrContext->purgeUnlockedResources(toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
@@ -150,7 +156,7 @@ void CacheManager::trimCaches(CacheTrimLevel mode) {
case CacheTrimLevel::ALL_CACHES:
SkGraphics::PurgeAllCaches();
if (mGrContext) {
- mGrContext->purgeUnlockedResources(false);
+ mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
}
break;
default:
@@ -163,7 +169,8 @@ void CacheManager::trimStaleResources() {
return;
}
mGrContext->flushAndSubmit();
- mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+ mGrContext->performDeferredCleanup(std::chrono::seconds(30),
+ GrPurgeResourceOptions::kAllResources);
}
void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
@@ -277,14 +284,15 @@ void CacheManager::onThreadIdle() {
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
// Rate limiting
- if ((now - mLastDeferredCleanup) < 25_ms) {
+ if ((now - mLastDeferredCleanup) > 25_ms) {
mLastDeferredCleanup = now;
const nsecs_t frameCompleteNanos = mFrameCompletions[0];
const nsecs_t frameDiffNanos = now - frameCompleteNanos;
const nsecs_t cleanupMillis =
- ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention));
+ ns2ms(std::clamp(frameDiffNanos, mMemoryPolicy.minimumResourceRetention,
+ mMemoryPolicy.maximumResourceRetention));
mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
- mMemoryPolicy.purgeScratchOnly);
+ toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
}
}
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 5e43ac209696..bcfa4f359d83 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -64,12 +64,13 @@ public:
void unregisterCanvasContext(CanvasContext* context);
void onContextStopped(CanvasContext* context);
+ bool areAllContextsStopped();
+
private:
friend class RenderThread;
explicit CacheManager(RenderThread& thread);
void setupCacheLimits();
- bool areAllContextsStopped();
void checkUiHidden();
void scheduleDestroyContext();
void cancelDestroyContext();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 310e39e1d0a2..b00fc699e515 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -125,6 +125,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mRenderPipeline(std::move(renderPipeline))
, mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) {
mRenderThread.cacheManager().registerCanvasContext(this);
+ mRenderThread.renderState().registerContextCallback(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
@@ -137,6 +138,7 @@ CanvasContext::~CanvasContext() {
}
mRenderNodes.clear();
mRenderThread.cacheManager().unregisterCanvasContext(this);
+ mRenderThread.renderState().removeContextCallback(this);
}
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -357,8 +359,9 @@ bool CanvasContext::makeCurrent() {
return true;
}
-static bool wasSkipped(FrameInfo* info) {
- return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
+static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) {
+ if (info) return info->getSkippedFrameReason();
+ return std::nullopt;
}
bool CanvasContext::isSwapChainStuffed() {
@@ -407,13 +410,26 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
- if (wasSkipped(mCurrentFrameInfo)) {
+ if (const auto reason = wasSkipped(mCurrentFrameInfo)) {
// Use the oldest skipped frame in case we skip more than a single frame
if (!mSkippedFrameInfo) {
- mSkippedFrameInfo.emplace();
- mSkippedFrameInfo->vsyncId =
- mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
- mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ switch (*reason) {
+ case SkippedFrameReason::AlreadyDrawn:
+ case SkippedFrameReason::NoBuffer:
+ case SkippedFrameReason::NoOutputTarget:
+ mSkippedFrameInfo.emplace();
+ mSkippedFrameInfo->vsyncId =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ mSkippedFrameInfo->startTime =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ break;
+ case SkippedFrameReason::DrawingOff:
+ case SkippedFrameReason::ContextIsStopped:
+ case SkippedFrameReason::NothingToDraw:
+ // Do not report those as skipped frames as there was no frame expected to be
+ // drawn
+ break;
+ }
}
} else {
mCurrentFrameInfo = mJankTracker.startFrame();
@@ -427,7 +443,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
info.damageGenerationId = mDamageId++;
- info.out.canDrawThisFrame = true;
+ info.out.skippedFrameReason = std::nullopt;
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -447,8 +463,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
mIsDirty = true;
if (CC_UNLIKELY(!hasOutputTarget())) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
return;
}
@@ -463,23 +479,23 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
if (vsyncDelta < 2_ms) {
// Already drew for this vsync pulse, UI draw request missed
// the deadline for RT animations
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn;
}
} else {
- info.out.canDrawThisFrame = true;
+ info.out.skippedFrameReason = std::nullopt;
}
// TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
// be an allowable combination?
if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw;
}
- if (info.out.canDrawThisFrame) {
+ if (!info.out.skippedFrameReason) {
int err = mNativeSurface->reserveNext();
if (err != OK) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
- info.out.canDrawThisFrame = false;
+ info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
if (err != TIMED_OUT) {
// A timed out surface can still recover, but assume others are permanently dead.
@@ -488,11 +504,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
}
}
} else {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
}
bool postedFrameCallback = false;
- if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+ if (info.out.hasAnimations || info.out.skippedFrameReason) {
if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
info.out.requiresUiRedraw = true;
}
@@ -558,9 +574,20 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) {
mSyncDelayDuration = 0;
mIdleDuration = 0;
- if (!Properties::isDrawingEnabled() ||
- (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
- mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> {
+ if (!Properties::isDrawingEnabled()) {
+ return SkippedFrameReason::DrawingOff;
+ }
+
+ if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+ return SkippedFrameReason::NothingToDraw;
+ }
+
+ return std::nullopt;
+ }();
+ if (skippedFrameReason) {
+ mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
// free its staging buffers.
@@ -891,10 +918,10 @@ const SkM44& CanvasContext::getPixelSnapMatrix() const {
}
void CanvasContext::prepareAndDraw(RenderNode* node) {
- ATRACE_CALL();
+ int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
+ ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
- int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline();
int64_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
@@ -904,7 +931,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
- if (info.out.canDrawThisFrame) {
+ if (!info.out.skippedFrameReason) {
draw(info.out.solelyTextureViewUpdates);
} else {
// wait on fences so tasks don't overlap next frame
@@ -965,6 +992,10 @@ void CanvasContext::destroyHardwareResources() {
}
}
+void CanvasContext::onContextDestroyed() {
+ destroyHardwareResources();
+}
+
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
return mRenderPipeline->createTextureLayer();
}
@@ -1103,6 +1134,12 @@ bool CanvasContext::shouldDither() {
return self->mColorMode != ColorMode::Default;
}
+void CanvasContext::visitAllRenderNodes(std::function<void(const RenderNode&)> func) const {
+ for (auto node : mRenderNodes) {
+ node->visit(func);
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 10a4afb23d35..37e4f7ecca54 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -43,6 +43,7 @@
#include "Lighting.h"
#include "ReliableSurface.h"
#include "RenderNode.h"
+#include "renderstate/RenderState.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "utils/RingBuffer.h"
@@ -64,7 +65,7 @@ class Frame;
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
// TODO: Rename to Renderer or some other per-window, top-level manager
-class CanvasContext : public IFrameCallback {
+class CanvasContext : public IFrameCallback, public IGpuContextCallback {
public:
static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, pid_t uiThreadId,
@@ -154,6 +155,7 @@ public:
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
+ void onContextDestroyed() override;
DeferredLayerUpdater* createTextureLayer();
@@ -236,6 +238,8 @@ public:
static bool shouldDither();
+ void visitAllRenderNodes(std::function<void(const RenderNode&)>) const;
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 53b43ba417d0..1b333bfccbf1 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -104,7 +104,7 @@ void DrawFrameTask::run() {
info.forceDrawFrame = mForceDrawFrame;
mForceDrawFrame = false;
canUnblockUiThread = syncFrameState(info);
- canDrawThisFrame = info.out.canDrawThisFrame;
+ canDrawThisFrame = !info.out.skippedFrameReason.has_value();
solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
if (mFrameCommitCallback) {
@@ -192,11 +192,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
if (CC_UNLIKELY(!hasTarget || !canDraw)) {
if (!hasTarget) {
mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
+ info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
} else {
// If we have a surface but can't draw we must be stopped
mSyncResult |= SyncResult::ContextIsStopped;
+ info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped;
}
- info.out.canDrawThisFrame = false;
}
if (info.out.hasAnimations) {
@@ -204,7 +205,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mSyncResult |= SyncResult::UIRedrawRequired;
}
}
- if (!info.out.canDrawThisFrame) {
+ if (info.out.skippedFrameReason) {
mSyncResult |= SyncResult::FrameDropped;
}
// If prepareTextures is false, we ran out of texture cache space
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 224c878bf43d..be163bad77a7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -16,7 +16,13 @@
#include "RenderProxy.h"
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkPicture.h>
#include <gui/TraceUtils.h>
+#include <pthread.h>
+#include <ui/GraphicBufferAllocator.h>
+
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
#include "Properties.h"
@@ -29,12 +35,6 @@
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
-#include <SkBitmap.h>
-#include <SkImage.h>
-#include <SkPicture.h>
-
-#include <pthread.h>
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -323,6 +323,11 @@ void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData, bool reset
}
});
}
+ if (!Properties::isolatedProcess) {
+ std::string grallocInfo;
+ GraphicBufferAllocator::getInstance().dump(grallocInfo);
+ dprintf(fd, "%s\n", grallocInfo.c_str());
+ }
}
void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 0dea941c91f9..94ed06c806e5 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -357,7 +357,15 @@ void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {
String8 cachesOutput;
mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
- dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.c_str());
+ dprintf(fd, "\nPipeline=%s\n%s", pipelineToString(), cachesOutput.c_str());
+ for (auto&& context : mCacheManager->mCanvasContexts) {
+ context->visitAllRenderNodes([&](const RenderNode& node) {
+ if (node.isTextureView()) {
+ dprintf(fd, "TextureView: %dx%d\n", node.getWidth(), node.getHeight());
+ }
+ });
+ }
+ dprintf(fd, "\n");
}
void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 46698a6fdcc0..90850d36b612 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -24,6 +24,8 @@
#include <GrTypes.h>
#include <android/sync.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
#include <ui/FatVector.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
@@ -33,9 +35,6 @@
#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
-#undef LOG_TAG
-#define LOG_TAG "VulkanManager"
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -386,25 +385,23 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
}
void VulkanManager::initialize() {
- std::lock_guard _lock{mInitializeLock};
-
- if (mDevice != VK_NULL_HANDLE) {
- return;
- }
+ std::call_once(mInitFlag, [&] {
+ GET_PROC(EnumerateInstanceVersion);
+ uint32_t instanceVersion;
+ LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
+ LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
- GET_PROC(EnumerateInstanceVersion);
- uint32_t instanceVersion;
- LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
- LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+ this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
- this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
- mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
- mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
+ if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ }
- if (Properties::enablePartialUpdates && Properties::useBufferAge) {
- mSwapBehavior = SwapBehavior::BufferAge;
- }
+ mInitialized = true;
+ });
}
static void onGrContextReleased(void* context) {
@@ -518,7 +515,7 @@ Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) {
// The following flush blocks the GPU immediately instead of waiting for
// other drawing ops. It seems dequeue_fence is not respected otherwise.
// TODO: remove the flush after finding why backendSemaphore is not working.
- bufferInfo->skSurface->flushAndSubmit();
+ skgpu::ganesh::FlushAndSubmit(bufferInfo->skSurface.get());
}
}
}
@@ -586,10 +583,10 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) {
} else {
semaphore = VK_NULL_HANDLE;
}
- GrSemaphoresSubmitted submitted =
- surface->flush(SkSurface::BackendSurfaceAccess::kPresent, flushInfo);
GrDirectContext* context = GrAsDirectContext(surface->recordingContext());
ALOGE_IF(!context, "Surface is not backed by gpu");
+ GrSemaphoresSubmitted submitted = context->flush(
+ surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
context->submit();
const nsecs_t submissionTime = systemTime();
if (semaphore != VK_NULL_HANDLE) {
@@ -599,10 +596,11 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) {
// retrieve VkImage used as render target
VkImage image = VK_NULL_HANDLE;
GrBackendRenderTarget backendRenderTarget =
- surface->getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess);
+ SkSurfaces::GetBackendRenderTarget(
+ surface, SkSurfaces::BackendHandleAccess::kFlushRead);
if (backendRenderTarget.isValid()) {
GrVkImageInfo info;
- if (backendRenderTarget.getVkImageInfo(&info)) {
+ if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
image = info.fImage;
} else {
ALOGE("Frame boundary: backend is not vulkan");
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 2be1ffdbc423..dbef7fbd51b2 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -70,7 +70,7 @@ public:
void initialize();
// Quick check to see if the VulkanManager has been initialized.
- bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
+ bool hasVkContext() { return mInitialized; }
// Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface
VulkanSurface* createSurface(ANativeWindow* window,
@@ -204,7 +204,8 @@ private:
VkSemaphore mSwapSemaphore = VK_NULL_HANDLE;
void* mDestroySemaphoreContext = nullptr;
- std::mutex mInitializeLock;
+ std::once_flag mInitFlag;
+ std::atomic_bool mInitialized = false;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 3168cb09291b..20b743bab2c2 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -16,6 +16,7 @@
#include "VulkanSurface.h"
+#include <include/android/SkSurfaceAndroid.h>
#include <GrDirectContext.h>
#include <SkSurface.h>
#include <algorithm>
@@ -24,9 +25,6 @@
#include "VulkanManager.h"
#include "utils/Color.h"
-#undef LOG_TAG
-#define LOG_TAG "VulkanSurface"
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -470,12 +468,12 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
surfaceProps = SkSurfaceProps(SkSurfaceProps::kAlwaysDither_Flag | surfaceProps.flags(),
surfaceProps.pixelGeometry());
}
- bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
+ bufferInfo->skSurface = SkSurfaces::WrapAndroidHardwareBuffer(
mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, &surfaceProps,
/*from_window=*/true);
if (bufferInfo->skSurface.get() == nullptr) {
- ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
+ ALOGE("SkSurfaces::WrapAndroidHardwareBuffer failed");
mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
mNativeBuffers[idx].dequeue_fence.release());
mNativeBuffers[idx].dequeued = false;
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 81ecfe59d3bc..ffc664c2e1bc 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,18 +61,10 @@ namespace uirenderer {
ADD_FAILURE() << "ClipState not a rect"; \
}
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
- TEST(test_case_name, test_name##_##pipeline) { \
- RenderPipelineType oldType = Properties::getRenderPipelineType(); \
- Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
- functionCall; \
- Properties::overrideRenderPipelineType(oldType); \
- };
-
-#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
- INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
- TestUtils::runOnRenderThread( \
- test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }
/**
* Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
@@ -83,21 +75,7 @@ namespace uirenderer {
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
- /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
- /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
- void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
- renderthread::RenderThread& renderThread)
-
-/**
- * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
- */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_RenderThreadTest { \
- public: \
- static void doTheThing(renderthread::RenderThread& renderThread); \
- }; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
@@ -307,13 +285,21 @@ public:
int destroyed = 0;
int removeOverlays = 0;
int glesDraw = 0;
+ int vkInitialize = 0;
+ int vkDraw = 0;
+ int vkPostDraw = 0;
};
static void expectOnRenderThread(const std::string_view& function = "unknown") {
EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function;
}
- static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
+ static int createMockFunctor() {
+ const auto renderMode = WebViewFunctor_queryPlatformRenderMode();
+ return WebViewFunctor_create(nullptr, createMockFunctorCallbacks(renderMode), renderMode);
+ }
+
+ static WebViewFunctorCallbacks createMockFunctorCallbacks(RenderMode mode) {
auto callbacks = WebViewFunctorCallbacks{
.onSync =
[](int functor, void* client_data, const WebViewSyncData& data) {
@@ -345,9 +331,22 @@ public:
sMockFunctorCounts[functor].glesDraw++;
};
break;
- default:
- ADD_FAILURE();
- return WebViewFunctorCallbacks{};
+ case RenderMode::Vulkan:
+ callbacks.vk.initialize = [](int functor, void* data,
+ const VkFunctorInitParams& params) {
+ expectOnRenderThread("initialize");
+ sMockFunctorCounts[functor].vkInitialize++;
+ };
+ callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params,
+ const WebViewOverlayData& overlayParams) {
+ expectOnRenderThread("draw");
+ sMockFunctorCounts[functor].vkDraw++;
+ };
+ callbacks.vk.postDraw = [](int functor, void* data) {
+ expectOnRenderThread("postDraw");
+ sMockFunctorCounts[functor].vkPostDraw++;
+ };
+ break;
}
return callbacks;
}
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f3f32eb6897c..e227999c2432 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -14,30 +14,32 @@
* limitations under the License.
*/
-#include "tests/common/LeakChecker.h"
-#include "tests/common/TestScene.h"
-
-#include "Properties.h"
-#include "hwui/Typeface.h"
-#include "HardwareBitmapUploader.h"
-#include "renderthread/RenderProxy.h"
-
+#include <android-base/parsebool.h>
#include <benchmark/benchmark.h>
+#include <errno.h>
+#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
+
+#include <regex>
#include <string>
#include <unordered_map>
#include <vector>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include "HardwareBitmapUploader.h"
+#include "Properties.h"
+#include "hwui/Typeface.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/LeakChecker.h"
+#include "tests/common/TestScene.h"
using namespace android;
+using namespace android::base;
using namespace android::uirenderer;
using namespace android::uirenderer::test;
@@ -69,6 +71,9 @@ OPTIONS:
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
+ --benchmark_list_tests Lists the tests that would run but does not run them
+ --benchmark_filter=<regex> Filters the test set to the given regex. If prefixed with `-` and test
+ that doesn't match the given regex is run
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
--skip-leak-check Skips the memory leak check
--report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run
@@ -140,6 +145,9 @@ static bool setBenchmarkFormat(const char* format) {
if (!strcmp(format, "tabular")) {
gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
} else if (!strcmp(format, "json")) {
+ // We cannot print the leak check if outputing to JSON as that will break
+ // JSON parsers since it's not JSON-formatted
+ gRunLeakCheck = false;
gBenchmarkReporter.reset(new benchmark::JSONReporter());
} else {
fprintf(stderr, "Unknown format '%s'\n", format);
@@ -160,6 +168,24 @@ static bool setRenderer(const char* renderer) {
return true;
}
+static void addTestsThatMatchFilter(std::string spec) {
+ if (spec.empty() || spec == "all") {
+ spec = "."; // Regexp that matches all benchmarks
+ }
+ bool isNegativeFilter = false;
+ if (spec[0] == '-') {
+ spec.replace(0, 1, "");
+ isNegativeFilter = true;
+ }
+ std::regex re(spec, std::regex_constants::extended);
+ for (auto& iter : TestScene::testMap()) {
+ if ((isNegativeFilter && !std::regex_search(iter.first, re)) ||
+ (!isNegativeFilter && std::regex_search(iter.first, re))) {
+ gRunTests.push_back(iter.second);
+ }
+ }
+}
+
// For options that only exist in long-form. Anything in the
// 0-255 range is reserved for short options (which just use their ASCII value)
namespace LongOpts {
@@ -170,6 +196,8 @@ enum {
ReportFrametime,
CpuSet,
BenchmarkFormat,
+ BenchmarkListTests,
+ BenchmarkFilter,
Onscreen,
Offscreen,
Renderer,
@@ -179,14 +207,16 @@ enum {
}
static const struct option LONG_OPTIONS[] = {
- {"frames", required_argument, nullptr, 'f'},
- {"repeat", required_argument, nullptr, 'r'},
+ {"count", required_argument, nullptr, 'c'},
+ {"runs", required_argument, nullptr, 'r'},
{"help", no_argument, nullptr, 'h'},
{"list", no_argument, nullptr, LongOpts::List},
{"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu},
{"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime},
{"cpuset", required_argument, nullptr, LongOpts::CpuSet},
{"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
+ {"benchmark_list_tests", optional_argument, nullptr, LongOpts::BenchmarkListTests},
+ {"benchmark_filter", required_argument, nullptr, LongOpts::BenchmarkFilter},
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
@@ -197,8 +227,12 @@ static const struct option LONG_OPTIONS[] = {
static const char* SHORT_OPTIONS = "c:r:h";
void parseOptions(int argc, char* argv[]) {
+ benchmark::BenchmarkReporter::Context::executable_name = (argc > 0) ? argv[0] : "unknown";
+
int c;
bool error = false;
+ bool listTestsOnly = false;
+ bool testsAreFiltered = false;
opterr = 0;
while (true) {
@@ -272,6 +306,21 @@ void parseOptions(int argc, char* argv[]) {
}
break;
+ case LongOpts::BenchmarkListTests:
+ if (!optarg || ParseBool(optarg) == ParseBoolResult::kTrue) {
+ listTestsOnly = true;
+ }
+ break;
+
+ case LongOpts::BenchmarkFilter:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ addTestsThatMatchFilter(optarg);
+ testsAreFiltered = true;
+ break;
+
case LongOpts::Renderer:
if (!optarg) {
error = true;
@@ -346,11 +395,18 @@ void parseOptions(int argc, char* argv[]) {
}
}
} while (optind < argc);
- } else {
+ } else if (gRunTests.empty() && !testsAreFiltered) {
for (auto& iter : TestScene::testMap()) {
gRunTests.push_back(iter.second);
}
}
+
+ if (listTestsOnly) {
+ for (auto& iter : gRunTests) {
+ std::cout << iter.name << std::endl;
+ }
+ exit(EXIT_SUCCESS);
+ }
}
int main(int argc, char* argv[]) {
diff --git a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
index 138b3efd10ed..b8b3f0aa5229 100644
--- a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
+++ b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
@@ -46,7 +46,7 @@ RENDERTHREAD_TEST(AutoBackendTextureRelease, makeImage_invalid) {
EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease));
- // SkImage::MakeFromTexture should fail if given null GrDirectContext.
+ // SkImages::BorrowTextureFrom should fail if given null GrDirectContext.
textureRelease->makeImage(buffer, HAL_DATASPACE_UNKNOWN, /*context = */ nullptr);
EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease));
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index 2b90bda87ecd..89d00d3c0dcb 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -20,7 +20,8 @@
#include "renderthread/EglManager.h"
#include "tests/common/TestUtils.h"
-#include <SkImagePriv.h>
+#include <SkImageAndroid.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include "include/gpu/GpuTypes.h" // from Skia
using namespace android;
@@ -34,7 +35,7 @@ static size_t getCacheUsage(GrDirectContext* grContext) {
}
// TOOD(258700630): fix this test and re-enable
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
+RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) {
int32_t width = DeviceInfo::get()->getWidth();
int32_t height = DeviceInfo::get()->getHeight();
GrDirectContext* grContext = renderThread.getGrContext();
@@ -46,8 +47,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) {
SkImageInfo info = SkImageInfo::MakeA8(width, height);
- sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, skgpu::Budgeted::kYes,
- info);
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kYes,
+ info);
surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT);
grContext->flushAndSubmit();
@@ -57,9 +58,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
// create an image and pin it so that we have something with a unique key in the cache
sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height));
- sk_sp<SkImage> image = bitmap->makeImage();
- ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
-
+ sk_sp<SkImage> image = bitmap->makeImage(); // calls skgpu::ganesh::PinAsTexture under the hood.
+ ASSERT_TRUE(skgpu::ganesh::PinAsTexture(grContext, image.get()));
// attempt to trim all memory while we still hold strong refs
renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
@@ -71,7 +71,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
}
// unpin the image which should add a unique purgeable key to the cache
- SkImage_unpinAsTexture(image.get(), grContext);
+ skgpu::ganesh::UnpinTexture(grContext, image.get());
// verify that we have enough purgeable bytes
const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes();
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 9e376e32f8ea..47a41057cac9 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -19,6 +19,7 @@
#include "AnimationContext.h"
#include "IContextFactory.h"
#include "renderthread/CanvasContext.h"
+#include "renderthread/VulkanManager.h"
#include "tests/common/TestUtils.h"
using namespace android;
@@ -42,3 +43,38 @@ RENDERTHREAD_TEST(CanvasContext, create) {
canvasContext->destroy();
}
+
+RENDERTHREAD_TEST(CanvasContext, buildLayerDoesntLeak) {
+ auto node = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFFFF0000, SkBlendMode::kSrc);
+ });
+ ASSERT_TRUE(node->isValid());
+ EXPECT_EQ(LayerType::None, node->stagingProperties().effectiveLayerType());
+ node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+
+ auto& cacheManager = renderThread.cacheManager();
+ EXPECT_TRUE(cacheManager.areAllContextsStopped());
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(
+ CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0));
+ canvasContext->buildLayer(node.get());
+ EXPECT_TRUE(node->hasLayer());
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ auto instance = VulkanManager::peekInstance();
+ if (instance) {
+ EXPECT_TRUE(instance->hasVkContext());
+ } else {
+ ADD_FAILURE() << "VulkanManager wasn't initialized to buildLayer?";
+ }
+ }
+ renderThread.destroyRenderingContext();
+ EXPECT_FALSE(node->hasLayer()) << "Node still has a layer after rendering context destroyed";
+
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ auto instance = VulkanManager::peekInstance();
+ if (instance) {
+ ADD_FAILURE() << "VulkanManager still exists";
+ EXPECT_FALSE(instance->hasVkContext());
+ }
+ }
+}
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 0c389bfe8b71..cfa18ae01b4f 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -40,7 +40,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
// push the deferred updates to the layer
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
- sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
+ sk_sp<SkImage> layerImage = SkImages::RasterFromBitmap(bitmap);
layerUpdater->updateLayer(true, layerImage, 0, SkRect::MakeEmpty());
// the backing layer should now have all the properties applied.
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
index 5a10f4f959aa..a14ae1cc46ec 100644
--- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -284,4 +284,4 @@ TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
EXPECT_EQ(mWrapper->alive(), true);
}
-} // namespace android::uirenderer::renderthread
+} // namespace android::uirenderer::renderthread \ No newline at end of file
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 596bd37e4cf5..073a8357e574 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -14,20 +14,22 @@
* limitations under the License.
*/
-#include <VectorDrawable.h>
-#include <gtest/gtest.h>
-
#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
+#include <VectorDrawable.h>
+#include <gtest/gtest.h>
+#include <include/effects/SkImageFilters.h>
#include <string.h>
+
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "FatalTestCanvas.h"
#include "IContextFactory.h"
-#include "hwui/Paint.h"
#include "RecordingCanvas.h"
#include "SkiaCanvas.h"
+#include "hwui/Paint.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
@@ -141,7 +143,7 @@ TEST(RenderNodeDrawable, zReorder) {
}
TEST(RenderNodeDrawable, composeOnLayer) {
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
SkCanvas& canvas = *surface->getCanvas();
canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
@@ -152,7 +154,7 @@ TEST(RenderNodeDrawable, composeOnLayer) {
});
// attach a layer to the render node
- auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surfaceLayer = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
auto canvas2 = surfaceLayer->getCanvas();
canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
rootNode->setLayerSurface(surfaceLayer);
@@ -187,7 +189,7 @@ static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
}
TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
- auto surface = SkSurface::MakeRasterN32Premul(400, 800);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(400, 800));
SkCanvas& canvas = *surface->getCanvas();
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
@@ -353,7 +355,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
EXPECT_EQ(3, canvas.getIndex());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
@@ -417,7 +419,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
EXPECT_EQ(2, canvas.getDrawCounter());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
/* R is backward projected on B and C is a layer.
A
/ \
@@ -1050,7 +1052,7 @@ TEST(RenderNodeDrawable, renderNode) {
}
// Verify that layers are composed with linear filtering.
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
static const int CANVAS_WIDTH = 1;
static const int CANVAS_HEIGHT = 1;
static const int LAYER_WIDTH = 1;
@@ -1074,7 +1076,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
});
layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
+ layerNode->setLayerSurface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(LAYER_WIDTH,
+ LAYER_HEIGHT)));
FrameTestCanvas canvas;
RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
@@ -1167,7 +1170,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
static const int CANVAS_WIDTH = 100;
static const int CANVAS_HEIGHT = 200;
class VectorDrawableTestCanvas : public TestCanvasBase {
@@ -1210,3 +1213,77 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
canvas.drawDrawable(&drawable);
EXPECT_EQ(2, canvas.mDrawCounter);
}
+
+// Verify drawing logics for BackdropFilterDrawable
+RENDERTHREAD_TEST(BackdropFilterDrawable, drawing) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 200;
+ class SimpleTestCanvas : public TestCanvasBase {
+ public:
+ SkRect mDstBounds;
+ SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ // did nothing.
+ }
+
+ // called when BackdropFilterDrawable is drawn.
+ void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
+ const SkSamplingOptions&, const SkPaint*,
+ SrcRectConstraint) override {
+ mDrawCounter++;
+ mDstBounds = dst;
+ }
+ };
+ class SimpleLayer : public SkSurface_Base {
+ public:
+ SimpleLayer()
+ : SkSurface_Base(SkImageInfo::MakeN32Premul(CANVAS_WIDTH, CANVAS_HEIGHT), nullptr) {
+ }
+ virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(CANVAS_WIDTH, CANVAS_HEIGHT);
+ bitmap.setImmutable();
+ return bitmap.asImage();
+ }
+ SkCanvas* onNewCanvas() override { return new SimpleTestCanvas(); }
+ sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
+ bool onCopyOnWrite(ContentChangeMode) override { return true; }
+ void onWritePixels(const SkPixmap&, int x, int y) {}
+ };
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ Paint());
+ });
+
+ sk_sp<SkSurface> surface(new SimpleLayer());
+ auto* canvas = reinterpret_cast<SimpleTestCanvas*>(surface->getCanvas());
+ RenderNodeDrawable drawable(node.get(), canvas, true);
+ BackdropFilterDrawable backdropDrawable(node.get(), canvas);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // no backdrop filter, skip drawing.
+ EXPECT_EQ(0, canvas->mDrawCounter);
+
+ sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
+ node->animatorProperties().mutateLayerProperties().setBackdropImageFilter(filter.get());
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // backdrop filter is set, ok to draw.
+ EXPECT_EQ(1, canvas->mDrawCounter);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds);
+
+ canvas->translate(30, 30);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // the drawable is still visible, ok to draw.
+ EXPECT_EQ(2, canvas->mDrawCounter);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH - 30, CANVAS_HEIGHT - 30), canvas->mDstBounds);
+
+ canvas->translate(CANVAS_WIDTH, CANVAS_HEIGHT);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // the drawable is invisible, skip drawing.
+ EXPECT_EQ(2, canvas->mDrawCounter);
+}
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 80796f4a4111..8273524167f9 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -231,8 +231,7 @@ TEST(RenderNode, multiTreeValidity) {
}
TEST(RenderNode, releasedCallback) {
- int functor = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ int functor = TestUtils::createMockFunctor();
auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) {
canvas.drawWebViewFunctor(functor);
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 9aa2e1db4461..0f8bd1368f5a 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -370,9 +370,9 @@ TEST(ShaderCacheTest, testCacheValidation) {
}
using namespace android::uirenderer;
-RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
- // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+ // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants.
GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
}
if (!folderExist(getExternalStorageFolder())) {
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 87c52161d68e..e53fcaa474e2 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -36,7 +36,7 @@ using namespace android;
using namespace android::uirenderer;
TEST(SkiaCanvas, drawShadowLayer) {
- auto surface = SkSurface::MakeRasterN32Premul(10, 10);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10));
SkiaCanvas canvas(surface->getCanvas());
// clear to white
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index f825d7c5d9cc..064d42ec8941 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -48,8 +48,7 @@ TEST(SkiaDisplayList, reset) {
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
- int functor1 = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ int functor1 = TestUtils::createMockFunctor();
GLFunctorDrawable functorDrawable{functor1, &dummyCanvas};
WebViewFunctor_release(functor1);
skiaDL->mChildFunctors.push_back(&functorDrawable);
@@ -101,8 +100,7 @@ TEST(SkiaDisplayList, syncContexts) {
SkCanvas dummyCanvas;
- int functor1 = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ int functor1 = TestUtils::createMockFunctor();
auto& counts = TestUtils::countsForFunctor(functor1);
skiaDL.mChildFunctors.push_back(
skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
@@ -131,6 +129,33 @@ TEST(SkiaDisplayList, syncContexts) {
EXPECT_EQ(counts.destroyed, 1);
}
+TEST(SkiaDisplayList, recordMutableBitmap) {
+ SkiaRecordingCanvas canvas{nullptr, 100, 100};
+ auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
+ 10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType));
+ EXPECT_FALSE(bitmap->isImmutable());
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+ auto displayList = canvas.finishRecording();
+ ASSERT_EQ(1, displayList->mMutableImages.size());
+ EXPECT_EQ(10, displayList->mMutableImages[0]->width());
+ EXPECT_EQ(20, displayList->mMutableImages[0]->height());
+}
+
+TEST(SkiaDisplayList, recordMutableBitmapInShader) {
+ SkiaRecordingCanvas canvas{nullptr, 100, 100};
+ auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
+ 10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType));
+ EXPECT_FALSE(bitmap->isImmutable());
+ SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ Paint paint;
+ paint.setShader(bitmap->makeImage()->makeShader(sampling));
+ canvas.drawPaint(paint);
+ auto displayList = canvas.finishRecording();
+ ASSERT_EQ(1, displayList->mMutableImages.size());
+ EXPECT_EQ(10, displayList->mMutableImages[0]->width());
+ EXPECT_EQ(20, displayList->mMutableImages[0]->height());
+}
+
class ContextFactory : public IContextFactory {
public:
virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
@@ -138,7 +163,7 @@ public:
}
};
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
@@ -197,7 +222,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
canvasContext->destroy();
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 4d0595e03da6..3ded540c3152 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -42,7 +42,7 @@ using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::skiapipeline;
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -54,7 +54,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
bool opaque = true;
android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
@@ -62,7 +62,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
Paint greenPaint;
@@ -76,7 +76,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
renderNodes.push_back(halfGreenNode);
android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2));
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface,
@@ -89,7 +89,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -100,7 +100,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
renderNodes.push_back(redNode);
android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2));
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface,
@@ -111,12 +111,12 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
});
- auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surfaceLayer1 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE);
redNode->setLayerSurface(surfaceLayer1);
@@ -127,7 +127,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
});
- auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2);
+ auto surfaceLayer2 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2));
surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE);
blueNode->setLayerSurface(surfaceLayer2);
@@ -154,7 +154,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
blueNode->setLayerSurface(sk_sp<SkSurface>());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
ScopedProperty<bool> prop(Properties::debugOverdraw, true);
auto whiteNode = TestUtils::createSkiaNode(
@@ -169,7 +169,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
// empty contentDrawBounds is avoiding backdrop/content logic, which would lead to less overdraw
android::uirenderer::Rect contentDrawBounds(0, 0, 0, 0);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
// Initialize the canvas to blue.
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
@@ -227,7 +227,7 @@ public:
};
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
class DeferTestCanvas : public SkCanvas {
public:
DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -297,7 +297,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 200;
class ClippedTestCanvas : public SkCanvas {
@@ -330,7 +330,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
}
// Test renderFrame with a dirty clip and a pre-transform matrix.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 100;
static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
@@ -366,7 +366,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
static const int CANVAS_WIDTH = 50;
static const int CANVAS_HEIGHT = 50;
class ClipReplaceTestCanvas : public SkCanvas {
@@ -396,7 +396,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+RENDERTHREAD_TEST(SkiaPipeline, context_lost) {
test::TestContext context;
auto surface = context.surface();
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
@@ -410,7 +410,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
EXPECT_TRUE(pipeline->isSurfaceReady());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) {
// create a pipeline and add a picture callback
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
int callbackCount = 0;
@@ -428,7 +428,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
renderNodes.push_back(redNode);
bool opaque = true;
android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
SkMatrix::I());
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
index e1fb8b7069ff..5e8f13d261c7 100644
--- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -26,9 +26,15 @@
using namespace android;
using namespace android::uirenderer;
+#define ASSUME_GLES() \
+ if (WebViewFunctor_queryPlatformRenderMode() != RenderMode::OpenGL_ES) \
+ GTEST_SKIP() << "Not in GLES, skipping test"
+
TEST(WebViewFunctor, createDestroyGLES) {
+ ASSUME_GLES();
int functor = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
WebViewFunctor_release(functor);
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
@@ -41,8 +47,10 @@ TEST(WebViewFunctor, createDestroyGLES) {
}
TEST(WebViewFunctor, createSyncHandleGLES) {
+ ASSUME_GLES();
int functor = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
@@ -82,8 +90,10 @@ TEST(WebViewFunctor, createSyncHandleGLES) {
}
TEST(WebViewFunctor, createSyncDrawGLES) {
+ ASSUME_GLES();
int functor = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
@@ -108,9 +118,11 @@ TEST(WebViewFunctor, createSyncDrawGLES) {
EXPECT_EQ(1, counts.destroyed);
}
-TEST(WebViewFunctor, contextDestroyed) {
+TEST(WebViewFunctor, contextDestroyedGLES) {
+ ASSUME_GLES();
int functor = WebViewFunctor_create(
- nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
+ nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
ASSERT_NE(-1, functor);
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 10c874ec3f12..76cbc8abc808 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <getopt.h>
+#include <signal.h>
#include "Properties.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
-#include <signal.h>
-
using namespace std;
using namespace android;
using namespace android::uirenderer;
@@ -45,6 +45,57 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
raise(sig);
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ Renderer,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}};
+
+static RenderPipelineType parseRenderer(const char* renderer) {
+ // Anything that's not skiavk is skiagl
+ if (!strcmp(renderer, "skiavk")) {
+ return RenderPipelineType::SkiaVulkan;
+ }
+ return RenderPipelineType::SkiaGL;
+}
+
+struct Options {
+ RenderPipelineType renderer = RenderPipelineType::SkiaGL;
+};
+
+Options parseOptions(int argc, char* argv[]) {
+ int c;
+ opterr = 0;
+ Options opts;
+
+ while (true) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index);
+
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
+
+ case LongOpts::Renderer:
+ opts.renderer = parseRenderer(optarg);
+ break;
+ }
+ }
+ return opts;
+}
+
class TypefaceEnvironment : public testing::Environment {
public:
virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); }
@@ -64,8 +115,9 @@ int main(int argc, char* argv[]) {
// Avoid talking to SF
Properties::isolatedProcess = true;
- // Default to GLES (Vulkan-aware tests will override this)
- Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+
+ auto opts = parseOptions(argc, argv);
+ Properties::overrideRenderPipelineType(opts.renderer);
// Run the tests
testing::InitGoogleTest(&argc, argv);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 6c0fd5f65359..5ce990fdeb82 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -23,6 +23,12 @@ package {
cc_library_shared {
name: "libinputservice",
+ defaults: [
+ // Build using the same flags and configurations as inputflinger.
+ "inputflinger_defaults",
+ ],
+ host_supported: false,
+
srcs: [
"PointerController.cpp",
"PointerControllerContext.cpp",
@@ -50,12 +56,4 @@ cc_library_shared {
],
include_dirs: ["frameworks/native/services"],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wthread-safety",
- ],
-
}
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index c3ad7670d473..6a465442c2b4 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -47,7 +47,7 @@ MouseCursorController::MouseCursorController(PointerControllerContext& context)
mLocked.pointerX = 0;
mLocked.pointerY = 0;
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
- mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
+ mLocked.pointerSprite = mContext.getSpriteController().createSprite();
mLocked.updatePointerIcon = false;
mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
@@ -325,8 +325,8 @@ bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(
}
if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
mLocked.animationFrameIndex += incr;
@@ -336,7 +336,7 @@ bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(
}
mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
// Keep animating.
return true;
@@ -346,8 +346,8 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
if (!mLocked.viewport.isValid()) {
return;
}
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
@@ -392,7 +392,7 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
mLocked.updatePointerIcon = false;
}
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index e21d6fb2fe14..abd928486607 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -63,10 +63,10 @@ void PointerController::DisplayInfoListener::onPointerControllerDestroyed() {
std::shared_ptr<PointerController> PointerController::create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController) {
+ SpriteController& spriteController, bool enabled) {
// using 'new' to access non-public constructor
std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
- new PointerController(policy, looper, spriteController));
+ new PointerController(policy, looper, spriteController, enabled));
/*
* Now we need to hook up the constructed PointerController object to its callbacks.
@@ -85,10 +85,10 @@ std::shared_ptr<PointerController> PointerController::create(
}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper,
- const sp<SpriteController>& spriteController)
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled)
: PointerController(
- policy, looper, spriteController,
+ policy, looper, spriteController, enabled,
[](const sp<android::gui::WindowInfosListener>& listener) {
SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
},
@@ -97,13 +97,13 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
}) {}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper,
- const sp<SpriteController>& spriteController,
- WindowListenerConsumer registerListener,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled, WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener)
- : mContext(policy, looper, spriteController, *this),
+ : mEnabled(enabled),
+ mContext(policy, looper, spriteController, *this),
mCursorController(mContext),
- mDisplayInfoListener(new DisplayInfoListener(this)),
+ mDisplayInfoListener(sp<DisplayInfoListener>::make(this)),
mUnregisterWindowInfosListener(std::move(unregisterListener)) {
std::scoped_lock lock(getLock());
mLocked.presentation = Presentation::SPOT;
@@ -121,10 +121,14 @@ std::mutex& PointerController::getLock() const {
}
std::optional<FloatRect> PointerController::getBounds() const {
+ if (!mEnabled) return {};
+
return mCursorController.getBounds();
}
void PointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -136,6 +140,8 @@ void PointerController::move(float deltaX, float deltaY) {
}
void PointerController::setPosition(float x, float y) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -147,6 +153,11 @@ void PointerController::setPosition(float x, float y) {
}
FloatPoint PointerController::getPosition() const {
+ if (!mEnabled) {
+ return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ }
+
const int32_t displayId = mCursorController.getDisplayId();
const auto p = mCursorController.getPosition();
{
@@ -157,20 +168,28 @@ FloatPoint PointerController::getPosition() const {
}
int32_t PointerController::getDisplayId() const {
+ if (!mEnabled) return ADISPLAY_ID_NONE;
+
return mCursorController.getDisplayId();
}
void PointerController::fade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.fade(transition);
}
void PointerController::unfade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.unfade(transition);
}
void PointerController::setPresentation(Presentation presentation) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
if (mLocked.presentation == presentation) {
@@ -195,6 +214,8 @@ void PointerController::setPresentation(Presentation presentation) {
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
const ui::Transform& transform = getTransformForDisplayLocked(displayId);
@@ -218,6 +239,8 @@ void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t
}
void PointerController::clearSpots() {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
clearSpotsLocked();
}
@@ -279,11 +302,15 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
}
void PointerController::updatePointerIcon(PointerIconStyle iconId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.updatePointerIcon(iconId);
}
void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.setCustomPointerIcon(icon);
}
@@ -292,7 +319,7 @@ void PointerController::doInactivityTimeout() {
fade(Transition::GRADUAL);
}
-void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
+void PointerController::onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
@@ -327,8 +354,12 @@ const ui::Transform& PointerController::getTransformForDisplayLocked(int display
return it != di.end() ? it->transform : kIdentityTransform;
}
-void PointerController::dump(std::string& dump) {
- dump += INDENT "PointerController:\n";
+std::string PointerController::dump() {
+ if (!mEnabled) {
+ return INDENT "PointerController: DISABLED due to ongoing PointerChoreographer refactor\n";
+ }
+
+ std::string dump = INDENT "PointerController:\n";
std::scoped_lock lock(getLock());
dump += StringPrintf(INDENT2 "Presentation: %s\n",
ftl::enum_string(mLocked.presentation).c_str());
@@ -341,6 +372,7 @@ void PointerController::dump(std::string& dump) {
for (const auto& [_, spotController] : mLocked.spotControllers) {
spotController.dump(dump, INDENT3);
}
+ return dump;
}
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 62ee74331302..aa7ca3c52ecf 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@ class PointerController : public PointerControllerInterface {
public:
static std::shared_ptr<PointerController> create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController);
+ SpriteController& spriteController, bool enabled);
~PointerController() override;
@@ -70,12 +70,12 @@ public:
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
void reloadPointerResources();
- void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports);
void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
- void dump(std::string& dump);
+ std::string dump();
protected:
using WindowListenerConsumer =
@@ -83,13 +83,13 @@ protected:
// Constructor used to test WindowInfosListener registration.
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController,
+ SpriteController& spriteController, bool enabled,
WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener);
private:
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController);
+ SpriteController& spriteController, bool enabled);
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -100,6 +100,8 @@ private:
// we use the DisplayInfoListener's lock in PointerController.
std::mutex& getLock() const;
+ const bool mEnabled;
+
PointerControllerContext mContext;
MouseCursorController mCursorController;
diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp
index f30e8d8e33a5..15c35176afce 100644
--- a/libs/input/PointerControllerContext.cpp
+++ b/libs/input/PointerControllerContext.cpp
@@ -32,12 +32,12 @@ namespace android {
PointerControllerContext::PointerControllerContext(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController, PointerController& controller)
+ SpriteController& spriteController, PointerController& controller)
: mPolicy(policy),
mLooper(looper),
mSpriteController(spriteController),
- mHandler(new MessageHandler()),
- mCallback(new LooperCallback()),
+ mHandler(sp<MessageHandler>::make()),
+ mCallback(sp<LooperCallback>::make()),
mController(controller),
mAnimator(*this) {
std::scoped_lock lock(mLock);
@@ -93,7 +93,7 @@ sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() {
return mPolicy;
}
-sp<SpriteController> PointerControllerContext::getSpriteController() {
+SpriteController& PointerControllerContext::getSpriteController() {
return mSpriteController;
}
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index f6f5d3bc51bd..98c3988e7df4 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -92,7 +92,7 @@ public:
class PointerControllerContext {
public:
PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController,
+ const sp<Looper>& looper, SpriteController& spriteController,
PointerController& controller);
~PointerControllerContext();
@@ -109,7 +109,7 @@ public:
void setCallbackController(std::shared_ptr<PointerController> controller);
sp<PointerControllerPolicyInterface> getPolicy();
- sp<SpriteController> getSpriteController();
+ SpriteController& getSpriteController();
void handleDisplayEvents();
@@ -163,7 +163,7 @@ private:
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
- sp<SpriteController> mSpriteController;
+ SpriteController& mSpriteController;
sp<MessageHandler> mHandler;
sp<LooperCallback> mCallback;
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 130b204954b4..6dc45a6aebec 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -31,12 +31,19 @@ SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLaye
ParentSurfaceProvider parentSurfaceProvider)
: mLooper(looper),
mOverlayLayer(overlayLayer),
+ mHandler(sp<Handler>::make()),
mParentSurfaceProvider(std::move(parentSurfaceProvider)) {
- mHandler = new WeakMessageHandler(this);
mLocked.transactionNestingCount = 0;
mLocked.deferredSpriteUpdate = false;
}
+void SpriteController::setHandlerController(
+ const std::shared_ptr<android::SpriteController>& controller) {
+ // Initialize the weak message handler outside the constructor, because we cannot get a shared
+ // pointer to self in the constructor.
+ mHandler->spriteController = controller;
+}
+
SpriteController::~SpriteController() {
mLooper->removeMessages(mHandler);
@@ -47,7 +54,7 @@ SpriteController::~SpriteController() {
}
sp<Sprite> SpriteController::createSprite() {
- return new SpriteImpl(this);
+ return sp<SpriteImpl>::make(*this);
}
void SpriteController::openTransaction() {
@@ -65,7 +72,7 @@ void SpriteController::closeTransaction() {
mLocked.transactionNestingCount -= 1;
if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
mLocked.deferredSpriteUpdate = false;
- mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES));
}
}
@@ -76,7 +83,7 @@ void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
} else {
- mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES));
}
}
}
@@ -85,18 +92,7 @@ void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceCon
bool wasEmpty = mLocked.disposedSurfaces.empty();
mLocked.disposedSurfaces.push_back(surfaceControl);
if (wasEmpty) {
- mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
- }
-}
-
-void SpriteController::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_UPDATE_SPRITES:
- doUpdateSprites();
- break;
- case MSG_DISPOSE_SURFACES:
- doDisposeSurfaces();
- break;
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_DISPOSE_SURFACES));
}
}
@@ -327,7 +323,7 @@ void SpriteController::doDisposeSurfaces() {
void SpriteController::ensureSurfaceComposerClient() {
if (mSurfaceComposerClient == NULL) {
- mSurfaceComposerClient = new SurfaceComposerClient();
+ mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
}
}
@@ -353,25 +349,41 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height
return surfaceControl;
}
-// --- SpriteController::SpriteImpl ---
+// --- SpriteController::Handler ---
-SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
- mController(controller) {
+void SpriteController::Handler::handleMessage(const android::Message& message) {
+ auto controller = spriteController.lock();
+ if (!controller) {
+ return;
+ }
+
+ switch (message.what) {
+ case MSG_UPDATE_SPRITES:
+ controller->doUpdateSprites();
+ break;
+ case MSG_DISPOSE_SURFACES:
+ controller->doDisposeSurfaces();
+ break;
+ }
}
+// --- SpriteController::SpriteImpl ---
+
+SpriteController::SpriteImpl::SpriteImpl(SpriteController& controller) : mController(controller) {}
+
SpriteController::SpriteImpl::~SpriteImpl() {
- AutoMutex _m(mController->mLock);
+ AutoMutex _m(mController.mLock);
// Let the controller take care of deleting the last reference to sprite
// surfaces so that we do not block the caller on an IPC here.
if (mLocked.state.surfaceControl != NULL) {
- mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
+ mController.disposeSurfaceLocked(mLocked.state.surfaceControl);
mLocked.state.surfaceControl.clear();
}
}
void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
uint32_t dirty;
if (icon.isValid()) {
@@ -401,7 +413,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
}
void SpriteController::SpriteImpl::setVisible(bool visible) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.visible != visible) {
mLocked.state.visible = visible;
@@ -410,7 +422,7 @@ void SpriteController::SpriteImpl::setVisible(bool visible) {
}
void SpriteController::SpriteImpl::setPosition(float x, float y) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
mLocked.state.positionX = x;
@@ -420,7 +432,7 @@ void SpriteController::SpriteImpl::setPosition(float x, float y) {
}
void SpriteController::SpriteImpl::setLayer(int32_t layer) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.layer != layer) {
mLocked.state.layer = layer;
@@ -429,7 +441,7 @@ void SpriteController::SpriteImpl::setLayer(int32_t layer) {
}
void SpriteController::SpriteImpl::setAlpha(float alpha) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.alpha != alpha) {
mLocked.state.alpha = alpha;
@@ -439,7 +451,7 @@ void SpriteController::SpriteImpl::setAlpha(float alpha) {
void SpriteController::SpriteImpl::setTransformationMatrix(
const SpriteTransformationMatrix& matrix) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.transformationMatrix != matrix) {
mLocked.state.transformationMatrix = matrix;
@@ -448,7 +460,7 @@ void SpriteController::SpriteImpl::setTransformationMatrix(
}
void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.displayId != displayId) {
mLocked.state.displayId = displayId;
@@ -461,7 +473,7 @@ void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
mLocked.state.dirty |= dirty;
if (!wasDirty) {
- mController->invalidateSpriteLocked(this);
+ mController.invalidateSpriteLocked(sp<SpriteImpl>::fromExisting(this));
}
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 1f113c045360..04ecb3895aa2 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -109,15 +109,19 @@ public:
*
* Clients are responsible for animating sprites by periodically updating their properties.
*/
-class SpriteController : public MessageHandler {
-protected:
- virtual ~SpriteController();
-
+class SpriteController {
public:
using ParentSurfaceProvider = std::function<sp<SurfaceControl>(int /*displayId*/)>;
SpriteController(const sp<Looper>& looper, int32_t overlayLayer, ParentSurfaceProvider parent);
+ SpriteController(const SpriteController&) = delete;
+ SpriteController& operator=(const SpriteController&) = delete;
+ virtual ~SpriteController();
- /* Creates a new sprite, initially invisible. */
+ /* Initialize the callback for the message handler. */
+ void setHandlerController(const std::shared_ptr<SpriteController>& controller);
+
+ /* Creates a new sprite, initially invisible. The lifecycle of the sprite must not extend beyond
+ * the lifecycle of this SpriteController. */
virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
@@ -129,9 +133,12 @@ public:
virtual void closeTransaction();
private:
- enum {
- MSG_UPDATE_SPRITES,
- MSG_DISPOSE_SURFACES,
+ class Handler : public virtual android::MessageHandler {
+ public:
+ enum { MSG_UPDATE_SPRITES, MSG_DISPOSE_SURFACES };
+
+ void handleMessage(const Message& message) override;
+ std::weak_ptr<SpriteController> spriteController;
};
enum {
@@ -192,7 +199,7 @@ private:
virtual ~SpriteImpl();
public:
- explicit SpriteImpl(const sp<SpriteController> controller);
+ explicit SpriteImpl(SpriteController& controller);
virtual void setIcon(const SpriteIcon& icon);
virtual void setVisible(bool visible);
@@ -220,7 +227,7 @@ private:
}
private:
- sp<SpriteController> mController;
+ SpriteController& mController;
struct Locked {
SpriteState state;
@@ -245,7 +252,7 @@ private:
sp<Looper> mLooper;
const int32_t mOverlayLayer;
- sp<WeakMessageHandler> mHandler;
+ sp<Handler> mHandler;
ParentSurfaceProvider mParentSurfaceProvider;
sp<SurfaceComposerClient> mSurfaceComposerClient;
@@ -260,7 +267,6 @@ private:
void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
- void handleMessage(const Message& message);
void doUpdateSprites();
void doDisposeSurfaces();
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index d9fe5996bcff..b8de919fbd8c 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -39,15 +39,15 @@ namespace android {
// --- Spot ---
-void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float newX, float newY,
int32_t displayId) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
- sprite->setPosition(x, y);
+ sprite->setPosition(newX, newY);
sprite->setDisplayId(displayId);
- this->x = x;
- this->y = y;
+ x = newX;
+ y = newY;
if (icon != mLastIcon) {
mLastIcon = icon;
@@ -98,8 +98,8 @@ void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32
#endif
std::scoped_lock lock(mLock);
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
// Add or move spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
@@ -125,7 +125,7 @@ void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32
}
}
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
void TouchSpotController::clearSpots() {
@@ -167,7 +167,7 @@ TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t
sprite = mLocked.recycledSprites.back();
mLocked.recycledSprites.pop_back();
} else {
- sprite = mContext.getSpriteController()->createSprite();
+ sprite = mContext.getSpriteController().createSprite();
}
// Return the new spot.
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 85747514aa03..94faf4a65a1c 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -157,7 +157,7 @@ protected:
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
- sp<MockSpriteController> mSpriteController;
+ std::unique_ptr<MockSpriteController> mSpriteController;
std::shared_ptr<PointerController> mPointerController;
private:
@@ -175,14 +175,14 @@ private:
PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
-
- mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+ mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
mPolicy = new MockPointerControllerPolicyInterface();
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
+ mPointerController =
+ PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
}
PointerControllerTest::~PointerControllerTest() {
@@ -319,10 +319,10 @@ class PointerControllerWindowInfoListenerTest : public Test {};
class TestPointerController : public PointerController {
public:
TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
- const sp<Looper>& looper)
+ const sp<Looper>& looper, SpriteController& spriteController)
: PointerController(
- new MockPointerControllerPolicyInterface(), looper,
- new NiceMock<MockSpriteController>(looper),
+ new MockPointerControllerPolicyInterface(), looper, spriteController,
+ /*enabled=*/true,
[&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
// Register listener
registeredListener = listener;
@@ -335,10 +335,12 @@ public:
TEST_F(PointerControllerWindowInfoListenerTest,
doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
+ sp<Looper> looper = new Looper(false);
+ auto spriteController = NiceMock<MockSpriteController>(looper);
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
{
- TestPointerController pointerController(registeredListener, new Looper(false));
+ TestPointerController pointerController(registeredListener, looper, spriteController);
ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
localListenerCopy = registeredListener;
}