summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/Android.bp11
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java66
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java7
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java28
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java43
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java28
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java17
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java5
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java22
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-core-release.aarbin0 -> 3714 bytes
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin32970 -> 34377 bytes
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml (renamed from libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml)0
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml)2
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml)6
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml)8
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml26
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml21
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml135
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml40
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml125
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml41
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml18
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml35
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml29
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml18
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml31
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml27
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml27
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml27
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml27
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml79
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-television/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-tvdpi/dimen.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values/colors_tv.xml17
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml7
-rw-r--r--libs/WindowManager/Shell/res/values/strings_tv.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java574
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java373
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java361
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java16
-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.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java)91
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java245
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java253
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java425
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java334
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java280
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java669
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java294
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java218
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java191
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java118
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java427
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt167
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt435
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt61
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt87
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt51
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt142
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt49
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt60
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt74
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt90
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt52
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt212
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt211
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt74
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt85
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt122
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt191
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt72
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt74
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt59
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt61
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt77
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt186
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt68
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt72
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt96
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt85
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt69
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt145
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt69
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt137
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt168
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt79
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt)37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt190
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt102
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt192
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt197
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt168
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt214
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt146
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt184
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt194
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt171
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt191
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt383
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt259
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt173
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt172
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt172
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt241
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp35
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml147
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.pngbin1966 -> 0 bytes
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml26
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml48
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml48
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml141
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml34
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java77
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java173
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java108
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java35
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java66
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java82
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java308
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java61
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java330
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java107
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java328
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java341
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt81
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java294
-rw-r--r--libs/androidfw/Android.bp9
-rw-r--r--[-rwxr-xr-x]libs/androidfw/ApkAssets.cpp13
-rw-r--r--libs/androidfw/AssetManager2.cpp231
-rw-r--r--libs/androidfw/AssetsProvider.cpp103
-rw-r--r--libs/androidfw/BigBuffer.cpp87
-rw-r--r--libs/androidfw/ConfigDescription.cpp2
-rw-r--r--libs/androidfw/Idmap.cpp76
-rw-r--r--libs/androidfw/LoadedArsc.cpp137
-rw-r--r--libs/androidfw/Locale.cpp6
-rw-r--r--libs/androidfw/PosixUtils.cpp54
-rw-r--r--libs/androidfw/ResourceTimer.cpp271
-rw-r--r--libs/androidfw/ResourceTypes.cpp109
-rw-r--r--libs/androidfw/ResourceUtils.cpp10
-rw-r--r--libs/androidfw/StringPool.cpp507
-rw-r--r--libs/androidfw/TypeWrappers.cpp13
-rw-r--r--libs/androidfw/Util.cpp145
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h20
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h31
-rw-r--r--libs/androidfw/include/androidfw/BigBuffer.h192
-rw-r--r--libs/androidfw/include/androidfw/ByteBucketArray.h58
-rw-r--r--libs/androidfw/include/androidfw/ConfigDescription.h2
-rw-r--r--libs/androidfw/include/androidfw/IDiagnostics.h130
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h29
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h21
-rw-r--r--libs/androidfw/include/androidfw/Locale.h6
-rw-r--r--libs/androidfw/include/androidfw/PosixUtils.h14
-rw-r--r--libs/androidfw/include/androidfw/ResourceTimer.h221
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h150
-rw-r--r--libs/androidfw/include/androidfw/ResourceUtils.h4
-rw-r--r--libs/androidfw/include/androidfw/Source.h90
-rw-r--r--libs/androidfw/include/androidfw/StringPiece.h292
-rw-r--r--libs/androidfw/include/androidfw/StringPool.h228
-rw-r--r--libs/androidfw/include/androidfw/Util.h135
-rw-r--r--libs/androidfw/include/androidfw/misc.h4
-rw-r--r--libs/androidfw/misc.cpp44
-rw-r--r--libs/androidfw/tests/AttributeResolution_bench.cpp4
-rw-r--r--libs/androidfw/tests/BigBuffer_test.cpp101
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp53
-rw-r--r--libs/androidfw/tests/ConfigDescription_test.cpp6
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp136
-rw-r--r--libs/androidfw/tests/PosixUtils_test.cpp18
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp68
-rw-r--r--libs/androidfw/tests/ResourceTimer_test.cpp245
-rw-r--r--libs/androidfw/tests/SparseEntry_bench.cpp67
-rw-r--r--libs/androidfw/tests/StringPiece_test.cpp32
-rw-r--r--libs/androidfw/tests/StringPool_test.cpp388
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp170
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin636 -> 732 bytes
-rw-r--r--libs/androidfw/tests/data/sparse/Android.bp23
-rw-r--r--libs/androidfw/tests/data/sparse/AndroidManifest.xml1
-rw-r--r--libs/androidfw/tests/data/sparse/R.h2
-rwxr-xr-xlibs/androidfw/tests/data/sparse/gen_strings.sh10
-rw-r--r--libs/androidfw/tests/data/sparse/not_sparse.apkbin62155 -> 62219 bytes
-rw-r--r--libs/androidfw/tests/data/sparse/res/values-land/strings.xml (renamed from libs/androidfw/tests/data/sparse/res/values-v26/strings.xml)2
-rw-r--r--libs/androidfw/tests/data/sparse/res/values-land/values.xml (renamed from libs/androidfw/tests/data/sparse/res/values-v26/values.xml)0
-rw-r--r--libs/androidfw/tests/data/sparse/sparse.apkbin59459 -> 59523 bytes
-rw-r--r--libs/hwui/Android.bp10
-rw-r--r--libs/hwui/AndroidTest.xml13
-rw-r--r--libs/hwui/CanvasTransform.cpp34
-rw-r--r--libs/hwui/CopyRequest.h42
-rw-r--r--libs/hwui/DeferredLayerUpdater.h1
-rw-r--r--libs/hwui/DeviceInfo.cpp10
-rw-r--r--libs/hwui/DeviceInfo.h6
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/FrameInfo.h1
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp9
-rw-r--r--libs/hwui/HardwareBitmapUploader.h3
-rw-r--r--libs/hwui/Layer.cpp2
-rw-r--r--libs/hwui/MemoryPolicy.cpp69
-rw-r--r--libs/hwui/MemoryPolicy.h62
-rw-r--r--libs/hwui/Properties.cpp8
-rw-r--r--libs/hwui/Properties.h13
-rw-r--r--libs/hwui/Readback.cpp114
-rw-r--r--libs/hwui/Readback.h20
-rw-r--r--libs/hwui/RecordingCanvas.cpp28
-rw-r--r--libs/hwui/RecordingCanvas.h6
-rw-r--r--libs/hwui/Rect.h9
-rw-r--r--libs/hwui/RenderNode.h1
-rw-r--r--libs/hwui/SkiaCanvas.cpp18
-rw-r--r--libs/hwui/SkiaCanvas.h11
-rw-r--r--libs/hwui/SkiaInterpolator.cpp14
-rw-r--r--libs/hwui/SkiaInterpolator.h12
-rw-r--r--libs/hwui/TEST_MAPPING3
-rw-r--r--libs/hwui/Tonemapper.cpp107
-rw-r--r--libs/hwui/Tonemapper.h (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java)21
-rw-r--r--libs/hwui/VectorDrawable.cpp5
-rw-r--r--libs/hwui/VectorDrawable.h1
-rw-r--r--libs/hwui/apex/LayoutlibLoader.cpp2
-rw-r--r--libs/hwui/apex/android_bitmap.cpp5
-rw-r--r--libs/hwui/apex/android_canvas.cpp2
-rw-r--r--libs/hwui/apex/android_paint.cpp1
-rw-r--r--libs/hwui/apex/jni_runtime.cpp115
-rw-r--r--libs/hwui/canvas/CanvasOps.h12
-rw-r--r--libs/hwui/hwui/Bitmap.cpp8
-rw-r--r--libs/hwui/hwui/Bitmap.h2
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp2
-rw-r--r--libs/hwui/hwui/Canvas.cpp1
-rw-r--r--libs/hwui/hwui/Canvas.h5
-rw-r--r--libs/hwui/hwui/ImageDecoder.h2
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp5
-rw-r--r--libs/hwui/hwui/Typeface.cpp13
-rw-r--r--libs/hwui/hwui/Typeface.h3
-rw-r--r--libs/hwui/jni/AnimatedImageDrawable.cpp3
-rwxr-xr-xlibs/hwui/jni/Bitmap.cpp54
-rw-r--r--libs/hwui/jni/Bitmap.h1
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp10
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp1
-rw-r--r--libs/hwui/jni/ByteBufferStreamAdaptor.cpp1
-rw-r--r--libs/hwui/jni/ColorFilter.cpp1
-rw-r--r--libs/hwui/jni/FontFamily.cpp5
-rw-r--r--libs/hwui/jni/GIFMovie.cpp2
-rw-r--r--libs/hwui/jni/Graphics.cpp36
-rw-r--r--libs/hwui/jni/GraphicsJNI.h31
-rw-r--r--libs/hwui/jni/HardwareBufferHelpers.cpp68
-rw-r--r--libs/hwui/jni/HardwareBufferHelpers.h38
-rw-r--r--libs/hwui/jni/ImageDecoder.cpp6
-rw-r--r--libs/hwui/jni/JvmErrorReporter.h44
-rw-r--r--libs/hwui/jni/MaskFilter.cpp1
-rw-r--r--libs/hwui/jni/Mesh.cpp227
-rw-r--r--libs/hwui/jni/Mesh.h262
-rw-r--r--libs/hwui/jni/MeshSpecification.cpp164
-rw-r--r--libs/hwui/jni/Movie.h1
-rw-r--r--libs/hwui/jni/MovieImpl.cpp9
-rw-r--r--libs/hwui/jni/NinePatch.cpp2
-rw-r--r--libs/hwui/jni/NinePatchPeeker.cpp2
-rw-r--r--libs/hwui/jni/Paint.cpp256
-rw-r--r--libs/hwui/jni/Path.cpp119
-rw-r--r--libs/hwui/jni/PathIterator.cpp81
-rw-r--r--libs/hwui/jni/RenderEffect.cpp1
-rw-r--r--libs/hwui/jni/Shader.cpp14
-rw-r--r--libs/hwui/jni/Typeface.cpp118
-rw-r--r--libs/hwui/jni/Utils.h3
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp2
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.h4
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp94
-rw-r--r--libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp177
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp150
-rw-r--r--libs/hwui/jni/fonts/Font.cpp11
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp15
-rw-r--r--libs/hwui/pipeline/skia/DumpOpsCanvas.h2
-rw-r--r--libs/hwui/pipeline/skia/HolePunch.h1
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp61
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp7
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h1
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp7
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp1
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h5
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp69
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h16
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp35
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h15
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp8
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.h9
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp29
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h7
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp52
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h23
-rw-r--r--libs/hwui/pipeline/skia/StretchMask.cpp4
-rw-r--r--libs/hwui/pipeline/skia/TransformCanvas.cpp10
-rw-r--r--libs/hwui/pipeline/skia/TransformCanvas.h7
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp184
-rw-r--r--libs/hwui/renderthread/CacheManager.h46
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp115
-rw-r--r--libs/hwui/renderthread/CanvasContext.h60
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp140
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h35
-rw-r--r--libs/hwui/renderthread/EglManager.h1
-rw-r--r--libs/hwui/renderthread/HardwareBufferRenderParams.h62
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp163
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.h57
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h17
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp52
-rw-r--r--libs/hwui/renderthread/RenderProxy.h17
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp11
-rw-r--r--libs/hwui/renderthread/RenderThread.h5
-rw-r--r--libs/hwui/renderthread/VulkanManager.h3
-rw-r--r--libs/hwui/renderthread/VulkanSurface.h1
-rw-r--r--libs/hwui/tests/common/CallCountingCanvas.h2
-rw-r--r--libs/hwui/tests/common/TestContext.cpp12
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.cpp2
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp6
-rw-r--r--libs/hwui/tests/common/TestUtils.h10
-rw-r--r--libs/hwui/tests/common/scenes/BitmapFillrate.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/BitmapShaders.cpp12
-rw-r--r--libs/hwui/tests/common/scenes/ClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmap565.cpp6
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/JankyScene.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/ListViewAnimation.cpp11
-rw-r--r--libs/hwui/tests/common/scenes/MagnifierAnimation.cpp55
-rw-r--r--libs/hwui/tests/common/scenes/OvalAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PathClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp6
-rw-r--r--libs/hwui/tests/common/scenes/RecentsAnimation.cpp5
-rw-r--r--libs/hwui/tests/common/scenes/RectGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShapeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp13
-rw-r--r--libs/hwui/tests/common/scenes/TextAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/TvApp.cpp5
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp2
-rw-r--r--libs/hwui/tests/microbench/RenderNodeBench.cpp2
-rw-r--r--libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp2
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp9
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp4
-rw-r--r--libs/hwui/tests/unit/CanvasOpTests.cpp11
-rw-r--r--libs/hwui/tests/unit/EglManagerTests.cpp2
-rw-r--r--libs/hwui/tests/unit/FatalTestCanvas.h2
-rw-r--r--libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp14
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp11
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp4
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp7
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp10
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp1
-rw-r--r--libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp1
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp72
-rw-r--r--libs/hwui/tests/unit/VectorDrawableTests.cpp6
-rw-r--r--libs/hwui/utils/PaintUtils.h1
-rw-r--r--libs/input/MouseCursorController.cpp36
-rw-r--r--libs/input/MouseCursorController.h8
-rw-r--r--libs/input/PointerController.cpp36
-rw-r--r--libs/input/PointerController.h5
-rw-r--r--libs/input/PointerControllerContext.h9
-rw-r--r--libs/input/SpriteController.cpp37
-rw-r--r--libs/input/SpriteController.h2
-rw-r--r--libs/input/SpriteIcon.h10
-rw-r--r--libs/input/TouchSpotController.cpp35
-rw-r--r--libs/input/TouchSpotController.h3
-rw-r--r--libs/input/tests/PointerController_test.cpp75
615 files changed, 20495 insertions, 10398 deletions
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index dc4b5636a246..a5b192cd7ceb 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -63,6 +63,12 @@ android_library_import {
sdk_version: "current",
}
+android_library_import {
+ name: "window-extensions-core",
+ aars: ["window-extensions-core-release.aar"],
+ sdk_version: "current",
+}
+
java_library {
name: "androidx.window.extensions",
srcs: [
@@ -70,7 +76,10 @@ java_library {
"src/androidx/window/util/**/*.java",
"src/androidx/window/common/**/*.java",
],
- static_libs: ["window-extensions"],
+ static_libs: [
+ "window-extensions",
+ "window-extensions-core",
+ ],
installable: true,
sdk_version: "core_platform",
system_ext_specific: true,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 54edd9ec4335..666b472c3716 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
- return 1;
+ return 2;
}
@NonNull
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 1cd3ea5592e3..8c4de70a7600 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -20,11 +20,11 @@ import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
@@ -77,6 +77,9 @@ import androidx.annotation.Nullable;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.extensions.WindowExtensionsImpl;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -87,7 +90,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Main controller class that manages split states and presentation.
@@ -113,7 +115,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* A developer-defined {@link SplitAttributes} calculator to compute the current
* {@link SplitAttributes} with the current device and window states.
- * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)}
+ * It is registered via {@link #setSplitAttributesCalculator(Function)}
* and unregistered via {@link #clearSplitAttributesCalculator()}.
* This is called when:
* <ul>
@@ -126,7 +128,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@GuardedBy("mLock")
@Nullable
- private SplitAttributesCalculator mSplitAttributesCalculator;
+ private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
/**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
@@ -139,6 +141,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
/** Callback to Jetpack to notify about changes to split states. */
+ @GuardedBy("mLock")
@Nullable
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
@@ -164,7 +167,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener());
}
- private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+ private class FoldingFeatureListener
+ implements java.util.function.Consumer<List<CommonFoldingFeature>> {
@Override
public void accept(List<CommonFoldingFeature> foldingFeatures) {
synchronized (mLock) {
@@ -205,7 +209,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+ public void setSplitAttributesCalculator(
+ @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) {
synchronized (mLock) {
mSplitAttributesCalculator = calculator;
}
@@ -220,7 +225,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
@Nullable
- SplitAttributesCalculator getSplitAttributesCalculator() {
+ Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
return mSplitAttributesCalculator;
}
@@ -233,9 +238,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Registers the split organizer callback to notify about changes to active splits.
+ * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
+ * {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
*/
+ @Deprecated
@Override
- public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+ public void setSplitInfoCallback(
+ @NonNull java.util.function.Consumer<List<SplitInfo>> callback) {
+ Consumer<List<SplitInfo>> oemConsumer = callback::accept;
+ setSplitInfoCallback(oemConsumer);
+ }
+
+ /**
+ * Registers the split organizer callback to notify about changes to active splits.
+ * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
+ */
+ public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
synchronized (mLock) {
mEmbeddingCallback = callback;
updateCallbackIfNecessary();
@@ -337,7 +355,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
container.setInfo(wct, taskFragmentInfo);
if (container.isFinished()) {
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else {
// Update with the latest Task configuration.
@@ -373,22 +392,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Do not finish the dependents if the last activity is reparented to PiP.
// Instead, the original split should be cleanup, and the dependent may be
// expanded to fullscreen.
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
cleanupForEnterPip(wct, container);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else if (taskFragmentInfo.isTaskClearedForReuse()) {
// Do not finish the dependents if this TaskFragment was cleared due to
// launching activity in the Task.
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else if (taskFragmentInfo.isClearedForReorderActivityToFront()) {
// Do not finish the dependents if this TaskFragment was cleared to reorder
// the launching Activity to front of the Task.
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else if (!container.isWaitingActivityAppear()) {
// Do not finish the container before the expected activity appear until
// timeout.
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, true /* shouldFinishDependent */);
}
} else if (wasInPip && isInPip) {
@@ -587,7 +611,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
container.setInfo(wct, taskFragmentInfo);
container.clearPendingAppearedActivities();
if (container.isEmpty()) {
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
}
break;
@@ -1004,7 +1029,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
void onTaskFragmentAppearEmptyTimeout(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
}
@@ -1596,7 +1622,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// (not resumed yet).
if (isOnCreated || primaryActivity.isResumed()) {
// Only set trigger type if the launch happens in foreground.
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_OPEN);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_OPEN);
return null;
}
final ActivityOptions options = ActivityOptions.makeBasic();
@@ -1624,7 +1651,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false;
}
- mTransactionManager.getCurrentTransactionRecord().setOriginType(TRANSIT_CLOSE);
+ mTransactionManager.getCurrentTransactionRecord()
+ .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
mPresenter.cleanupContainer(wct, splitContainer.getSecondaryContainer(),
false /* shouldFinishDependent */);
return true;
@@ -1928,7 +1956,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
synchronized (mLock) {
final TransactionRecord transactionRecord = mTransactionManager
.startNewTransaction();
- transactionRecord.setOriginType(TRANSIT_OPEN);
+ transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_OPEN);
SplitController.this.onActivityCreated(transactionRecord.getTransaction(),
activity);
// The WCT should be applied and merged to the activity launch transition.
@@ -2018,7 +2046,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
synchronized (mLock) {
final TransactionRecord transactionRecord = mTransactionManager
.startNewTransaction();
- transactionRecord.setOriginType(TRANSIT_OPEN);
+ transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_OPEN);
final WindowContainerTransaction wct = transactionRecord.getTransaction();
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 14d244bbb6ce..6f2fe80481c7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -43,11 +43,11 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
-import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams;
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -551,7 +551,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
- final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
+ final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
+ mController.getSplitAttributesCalculator();
final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
if (calculator == null) {
@@ -567,7 +568,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
taskWindowMetrics, taskConfiguration, defaultSplitAttributes,
isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag());
- final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params);
+ final SplitAttributes splitAttributes = calculator.apply(params);
return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
index 0071fea41aa8..396956e56bb5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
@@ -16,12 +16,12 @@
package androidx.window.extensions.embedding;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_NONE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_NONE;
import android.os.IBinder;
-import android.view.WindowManager.TransitionType;
import android.window.TaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer.TaskFragmentTransitionType;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -122,8 +122,8 @@ class TransactionManager {
* @see #setOriginType(int)
* @see #getTransactionTransitionType()
*/
- @TransitionType
- private int mOriginType = TRANSIT_NONE;
+ @TaskFragmentTransitionType
+ private int mOriginType = TASK_FRAGMENT_TRANSIT_NONE;
TransactionRecord(@Nullable IBinder taskFragmentTransactionToken) {
mTaskFragmentTransactionToken = taskFragmentTransactionToken;
@@ -136,12 +136,12 @@ class TransactionManager {
}
/**
- * Sets the {@link TransitionType} that triggers this transaction. If there are multiple
- * calls, only the first call will be respected as the "origin" type.
+ * Sets the {@link TaskFragmentTransitionType} that triggers this transaction. If there are
+ * multiple calls, only the first call will be respected as the "origin" type.
*/
- void setOriginType(@TransitionType int type) {
+ void setOriginType(@TaskFragmentTransitionType int type) {
ensureCurrentTransaction();
- if (mOriginType != TRANSIT_NONE) {
+ if (mOriginType != TASK_FRAGMENT_TRANSIT_NONE) {
// Skip if the origin type has already been set.
return;
}
@@ -188,14 +188,16 @@ class TransactionManager {
}
/**
- * Gets the {@link TransitionType} that we will request transition with for the
+ * Gets the {@link TaskFragmentTransitionType} that we will request transition with for the
* current {@link WindowContainerTransaction}.
*/
@VisibleForTesting
- @TransitionType
+ @TaskFragmentTransitionType
int getTransactionTransitionType() {
- // Use TRANSIT_CHANGE as default if there is not opening/closing window.
- return mOriginType != TRANSIT_NONE ? mOriginType : TRANSIT_CHANGE;
+ // Use TASK_FRAGMENT_TRANSIT_CHANGE as default if there is not opening/closing window.
+ return mOriginType != TASK_FRAGMENT_TRANSIT_NONE
+ ? mOriginType
+ : TASK_FRAGMENT_TRANSIT_CHANGE;
}
}
}
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 c9f870005eb9..8386131b177d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -45,6 +45,7 @@ import androidx.annotation.UiContext;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.util.DataProducer;
import java.util.ArrayList;
@@ -53,7 +54,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Reference implementation of androidx.window.extensions.layout OEM interface for use with
@@ -82,6 +82,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
+ mJavaToExtConsumers = new ArrayMap<>();
+
private final TaskFragmentOrganizer mTaskFragmentOrganizer;
public WindowLayoutComponentImpl(@NonNull Context context,
@@ -95,7 +99,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
/** Registers to listen to {@link CommonFoldingFeature} changes */
- public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
+ public void addFoldingStateChangedCallback(
+ java.util.function.Consumer<List<CommonFoldingFeature>> consumer) {
synchronized (mLock) {
mFoldingFeatureProducer.addDataChangedCallback(consumer);
}
@@ -109,13 +114,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
@Override
public void addWindowLayoutInfoListener(@NonNull Activity activity,
- @NonNull Consumer<WindowLayoutInfo> consumer) {
- addWindowLayoutInfoListener((Context) activity, consumer);
+ @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+ final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
+ synchronized (mLock) {
+ mJavaToExtConsumers.put(consumer, extConsumer);
+ }
+ addWindowLayoutInfoListener(activity, extConsumer);
+ }
+
+ @Override
+ public void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
+ @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+ final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
+ synchronized (mLock) {
+ mJavaToExtConsumers.put(consumer, extConsumer);
+ }
+ addWindowLayoutInfoListener(context, extConsumer);
}
/**
- * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
- * as a parameter.
+ * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but
+ * takes a UI Context as a parameter.
*
* Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all
* consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo}
@@ -156,6 +175,18 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
}
+ @Override
+ public void removeWindowLayoutInfoListener(
+ @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+ final Consumer<WindowLayoutInfo> extConsumer;
+ synchronized (mLock) {
+ extConsumer = mJavaToExtConsumers.remove(consumer);
+ }
+ if (extConsumer != null) {
+ removeWindowLayoutInfoListener(extConsumer);
+ }
+ }
+
/**
* Removes a listener no longer interested in receiving updates.
*
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index 2f92a577baa2..459ec9f89c4a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -34,9 +34,11 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Pair;
+import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerToken;
+import androidx.window.extensions.core.util.function.Predicate;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -107,7 +109,7 @@ public class EmbeddingTestUtils {
static SplitRule createSplitRule(@NonNull Activity primaryActivity,
@NonNull Intent secondaryIntent, boolean clearTop) {
final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
- return new SplitPairRule.Builder(
+ return createSplitPairRuleBuilder(
activityPair -> false,
targetPair::equals,
w -> true)
@@ -144,7 +146,7 @@ public class EmbeddingTestUtils {
@NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
int finishSecondaryWithPrimary, boolean clearTop) {
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
- return new SplitPairRule.Builder(
+ return createSplitPairRuleBuilder(
targetPair::equals,
activityIntentPair -> false,
w -> true)
@@ -223,4 +225,26 @@ public class EmbeddingTestUtils {
displayFeatures.add(foldingFeature);
return new WindowLayoutInfo(displayFeatures);
}
+
+ static ActivityRule.Builder createActivityBuilder(
+ @NonNull Predicate<Activity> activityPredicate,
+ @NonNull Predicate<Intent> intentPredicate) {
+ return new ActivityRule.Builder(activityPredicate, intentPredicate);
+ }
+
+ static SplitPairRule.Builder createSplitPairRuleBuilder(
+ @NonNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate,
+ @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate,
+ @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+ return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate,
+ windowMetricsPredicate);
+ }
+
+ static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder(
+ @NonNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate,
+ @NonNull Predicate<Intent> intentPredicate,
+ @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+ return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
+ intentPredicate, windowMetricsPredicate);
+ }
}
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 81c39571bffa..0bf0bc85b511 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
@@ -34,8 +34,11 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTR
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -432,7 +435,7 @@ public class SplitControllerTest {
@Test
public void testResolveStartActivityIntent_withoutLaunchingActivity() {
final Intent intent = new Intent();
- final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent)
+ final ActivityRule expandRule = createActivityBuilder(r -> false, i -> i == intent)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1170,7 +1173,7 @@ public class SplitControllerTest {
@Test
public void testHasSamePresentation() {
- SplitPairRule splitRule1 = new SplitPairRule.Builder(
+ SplitPairRule splitRule1 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1178,7 +1181,7 @@ public class SplitControllerTest {
.setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
- SplitPairRule splitRule2 = new SplitPairRule.Builder(
+ SplitPairRule splitRule2 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1191,7 +1194,7 @@ public class SplitControllerTest {
SplitController.haveSamePresentation(splitRule1, splitRule2,
new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
- splitRule2 = new SplitPairRule.Builder(
+ splitRule2 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1355,7 +1358,7 @@ public class SplitControllerTest {
/** Setups a rule to always expand the given intent. */
private void setupExpandRule(@NonNull Intent expandIntent) {
- final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals)
+ final ActivityRule expandRule = createActivityBuilder(r -> false, expandIntent::equals)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1363,7 +1366,7 @@ public class SplitControllerTest {
/** Setups a rule to always expand the given activity. */
private void setupExpandRule(@NonNull Activity expandActivity) {
- final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false)
+ final ActivityRule expandRule = createActivityBuilder(expandActivity::equals, i -> false)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1371,7 +1374,7 @@ public class SplitControllerTest {
/** Setups a rule to launch placeholder for the given activity. */
private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
- final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
+ final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT,
primaryActivity::equals, i -> false, w -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 121e81394b2d..ff1256b47429 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -28,6 +28,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUND
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -511,7 +512,7 @@ public class SplitPresenterTest {
final Activity secondaryActivity = createMockActivity();
final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
- final SplitPairRule rule = new SplitPairRule.Builder(pair ->
+ final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
metrics -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
@@ -529,7 +530,7 @@ public class SplitPresenterTest {
@Test
public void testComputeSplitAttributes() {
- final SplitPairRule splitPairRule = new SplitPairRule.Builder(
+ final SplitPairRule splitPairRule = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS))
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
index 62006bd51399..459b6d2c31f9 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
@@ -16,9 +16,9 @@
package androidx.window.extensions.embedding;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE;
+import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
@@ -86,27 +86,29 @@ public class TransactionManagerTest {
@Test
public void testSetTransactionOriginType() {
- // Return TRANSIT_CHANGE if there is no trigger type set.
+ // Return TASK_FRAGMENT_TRANSIT_CHANGE if there is no trigger type set.
TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
- assertEquals(TRANSIT_CHANGE, transactionRecord.getTransactionTransitionType());
+ assertEquals(TASK_FRAGMENT_TRANSIT_CHANGE,
+ transactionRecord.getTransactionTransitionType());
// Return the first set type.
mTransactionManager.getCurrentTransactionRecord().abort();
transactionRecord = mTransactionManager.startNewTransaction();
- transactionRecord.setOriginType(TRANSIT_OPEN);
+ transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_OPEN);
- assertEquals(TRANSIT_OPEN, transactionRecord.getTransactionTransitionType());
+ assertEquals(TASK_FRAGMENT_TRANSIT_OPEN, transactionRecord.getTransactionTransitionType());
- transactionRecord.setOriginType(TRANSIT_CLOSE);
+ transactionRecord.setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
- assertEquals(TRANSIT_OPEN, transactionRecord.getTransactionTransitionType());
+ assertEquals(TASK_FRAGMENT_TRANSIT_OPEN, transactionRecord.getTransactionTransitionType());
// Reset when #startNewTransaction().
transactionRecord.abort();
transactionRecord = mTransactionManager.startNewTransaction();
- assertEquals(TRANSIT_CHANGE, transactionRecord.getTransactionTransitionType());
+ assertEquals(TASK_FRAGMENT_TRANSIT_CHANGE,
+ transactionRecord.getTransactionTransitionType());
}
@Test
diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
new file mode 100644
index 000000000000..96ff840b984b
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 84ab4487feee..a939cd8a1745 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 8881be7326a9..36d3313a9f3b 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -21,5 +21,6 @@
<uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
</manifest>
diff --git a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml
index 7475abac4695..7475abac4695 100644
--- a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml
+++ b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml
index ce8640df0093..67467bbc72ae 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@color/tv_pip_menu_icon_unfocused" />
+ <item android:color="@color/tv_window_menu_icon_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml
index 4f5e63dac5c0..4182bfeefa1b 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ 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.
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_icon_bg_focused" />
- <item android:color="@color/tv_pip_menu_icon_bg_unfocused" />
+ android:color="@color/tv_window_menu_close_icon_bg_focused" />
+ <item android:color="@color/tv_window_menu_close_icon_bg_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml
index 275870450493..45205d2a7138 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ 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.
@@ -16,8 +16,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_icon_focused" />
+ android:color="@color/tv_window_menu_icon_focused" />
<item android:state_enabled="false"
- android:color="@color/tv_pip_menu_icon_disabled" />
- <item android:color="@color/tv_pip_menu_icon_unfocused" />
+ android:color="@color/tv_window_menu_icon_disabled" />
+ <item android:color="@color/tv_window_menu_icon_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml
index 6cbf66f00df7..1bd26e1d6583 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_close_icon_bg_focused" />
- <item android:color="@color/tv_pip_menu_close_icon_bg_unfocused" />
+ android:color="@color/tv_window_menu_icon_bg_focused" />
+ <item android:color="@color/tv_window_menu_icon_bg_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
deleted file mode 100644
index 1938f4562e97..000000000000
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="@dimen/pip_menu_button_radius" />
- <solid android:color="@color/tv_pip_menu_icon_bg" />
-</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
index 846fdb3e8a58..7085a2c72c86 100644
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:exitFadeDuration="@integer/pip_menu_fade_animation_duration">
+ android:exitFadeDuration="@integer/tv_window_menu_fade_animation_duration">
<item android:state_activated="true">
<shape android:shape="rectangle">
<corners android:radius="@dimen/pip_menu_border_corner_radius" />
diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml
new file mode 100644
index 000000000000..a348b148afb4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/tv_window_menu_icon_size"
+ android:height="@dimen/tv_window_menu_icon_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M17,4h3c1.1,0 2,0.9 2,2v2h-2L20,6h-3L17,4zM4,8L4,6h3L7,4L4,4c-1.1,0 -2,0.9 -2,2v2h2zM20,16v2h-3v2h3c1.1,0 2,-0.9 2,-2v-2h-2zM7,18L4,18v-2L2,16v2c0,1.1 0.9,2 2,2h3v-2zM18,8L6,8v8h12L18,8z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml
new file mode 100644
index 000000000000..c5d54c5fa4f2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/tv_window_menu_icon_size"
+ android:height="@dimen/tv_window_menu_icon_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml
new file mode 100644
index 000000000000..2dba37daf059
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/tv_window_menu_button_radius" />
+ <solid android:color="@color/tv_window_menu_icon_bg" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 7a3ee23d8cdc..dcce4698c252 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -16,92 +16,46 @@
-->
<!-- Layout for TvPipMenuView -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tv_pip_menu"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center|top">
+ android:id="@+id/tv_pip_menu"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center|top">
<!-- Matches the PiP app content -->
- <View
+ <FrameLayout
android:id="@+id/tv_pip"
android:layout_width="0dp"
android:layout_height="0dp"
- android:alpha="0"
- android:background="@color/tv_pip_menu_background"
android:layout_marginTop="@dimen/pip_menu_outer_space"
android:layout_marginStart="@dimen/pip_menu_outer_space"
- android:layout_marginEnd="@dimen/pip_menu_outer_space"/>
+ android:layout_marginEnd="@dimen/pip_menu_outer_space">
- <ScrollView
- android:id="@+id/tv_pip_menu_scroll"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignTop="@+id/tv_pip"
- android:layout_alignStart="@+id/tv_pip"
- android:layout_alignEnd="@+id/tv_pip"
- android:layout_alignBottom="@+id/tv_pip"
- android:scrollbars="none"
- android:visibility="gone"/>
+ <View
+ android:id="@+id/tv_pip_menu_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tv_pip_menu_background"
+ android:alpha="0"/>
- <HorizontalScrollView
- android:id="@+id/tv_pip_menu_horizontal_scroll"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignTop="@+id/tv_pip"
- android:layout_alignStart="@+id/tv_pip"
- android:layout_alignEnd="@+id/tv_pip"
- android:layout_alignBottom="@+id/tv_pip"
- android:scrollbars="none">
+ <View
+ android:id="@+id/tv_pip_menu_dim_layer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tv_pip_menu_dim_layer"
+ android:alpha="0"/>
- <LinearLayout
+ <com.android.internal.widget.RecyclerView
android:id="@+id/tv_pip_menu_action_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:alpha="0">
-
- <Space
- android:layout_width="@dimen/pip_menu_button_wrapper_margin"
- android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
-
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_fullscreen_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_fullscreen_white"
- android:text="@string/pip_fullscreen" />
-
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_close_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
-
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
-
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_move_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_move_white"
- android:text="@string/pip_move" />
-
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_collapse"
- android:visibility="gone"
- android:text="@string/pip_collapse" />
-
- <Space
- android:layout_width="@dimen/pip_menu_button_wrapper_margin"
- android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
-
- </LinearLayout>
- </HorizontalScrollView>
+ android:layout_gravity="center"
+ android:padding="@dimen/pip_menu_button_start_end_offset"
+ android:clipToPadding="false"
+ android:alpha="0"
+ android:contentDescription="@string/a11y_pip_menu_entered"/>
+ </FrameLayout>
+ <!-- Frame around the content, just overlapping the corners to make them round -->
<View
android:id="@+id/tv_pip_border"
android:layout_width="0dp"
@@ -111,8 +65,9 @@
android:layout_marginEnd="@dimen/pip_menu_outer_space_frame"
android:background="@drawable/tv_pip_menu_border"/>
+ <!-- Temporarily extending the background to show an edu text hint for opening the menu -->
<FrameLayout
- android:id="@+id/tv_pip_menu_edu_text_container"
+ android:id="@+id/tv_pip_menu_edu_text_drawer_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tv_pip"
@@ -120,23 +75,10 @@
android:layout_alignStart="@+id/tv_pip"
android:layout_alignEnd="@+id/tv_pip"
android:background="@color/tv_pip_menu_background"
- android:clipChildren="true">
-
- <TextView
- android:id="@+id/tv_pip_menu_edu_text"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/pip_menu_edu_text_view_height"
- android:layout_gravity="bottom|center"
- android:gravity="center"
- android:paddingBottom="@dimen/pip_menu_border_width"
- android:text="@string/pip_edu_text"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="1"
- android:scrollHorizontally="true"
- android:textAppearance="@style/TvPipEduText"/>
- </FrameLayout>
+ android:paddingBottom="@dimen/pip_menu_border_width"
+ android:paddingTop="@dimen/pip_menu_border_width"/>
+ <!-- Frame around the PiP content + edu text hint - used to highlight open menu -->
<View
android:id="@+id/tv_pip_menu_frame"
android:layout_width="match_parent"
@@ -144,6 +86,17 @@
android:layout_margin="@dimen/pip_menu_outer_space_frame"
android:background="@drawable/tv_pip_menu_border"/>
+ <!-- Move menu -->
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_pip_menu_done_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/pip_ic_close_white"
+ android:visibility="gone"
+ android:text="@string/a11y_action_pip_move_done" />
+
<ImageView
android:id="@+id/tv_pip_menu_arrow_up"
android:layout_width="@dimen/pip_menu_arrow_size"
@@ -151,6 +104,7 @@
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:alpha="0"
+ android:contentDescription="@string/a11y_action_pip_move_up"
android:elevation="@dimen/pip_menu_arrow_elevation"
android:src="@drawable/pip_ic_move_up" />
@@ -161,6 +115,7 @@
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:alpha="0"
+ android:contentDescription="@string/a11y_action_pip_move_right"
android:elevation="@dimen/pip_menu_arrow_elevation"
android:src="@drawable/pip_ic_move_right" />
@@ -171,6 +126,7 @@
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:alpha="0"
+ android:contentDescription="@string/a11y_action_pip_move_down"
android:elevation="@dimen/pip_menu_arrow_elevation"
android:src="@drawable/pip_ic_move_down" />
@@ -181,6 +137,7 @@
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:alpha="0"
+ android:contentDescription="@string/a11y_action_pip_move_left"
android:elevation="@dimen/pip_menu_arrow_elevation"
android:src="@drawable/pip_ic_move_left" />
</RelativeLayout>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
deleted file mode 100644
index db96d8de4094..000000000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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.
--->
-<!-- Layout for TvPipMenuActionButton -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/button"
- android:layout_width="@dimen/pip_menu_button_size"
- android:layout_height="@dimen/pip_menu_button_size"
- android:padding="@dimen/pip_menu_button_margin"
- android:stateListAnimator="@animator/tv_pip_menu_action_button_animator"
- android:focusable="true">
-
- <View android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:duplicateParentState="true"
- android:background="@drawable/tv_pip_button_bg"/>
-
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/pip_menu_icon_size"
- android:layout_height="@dimen/pip_menu_icon_size"
- android:layout_gravity="center"
- android:duplicateParentState="true"
- android:tint="@color/tv_pip_menu_icon" />
-</FrameLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml
new file mode 100644
index 000000000000..e0fa59c9f157
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml
@@ -0,0 +1,125 @@
+<!--
+ ~ 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.
+ -->
+<!-- Layout for TvSplitMenuView -->
+<com.android.wm.shell.splitscreen.tv.TvSplitMenuView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <LinearLayout
+ android:id="@+id/tv_split_main_menu"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_main_menu_focus_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_focus" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_main_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_menu_swap_stages"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_swap" />
+
+ <LinearLayout
+ android:id="@+id/tv_split_side_menu"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_side_menu_focus_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/tv_split_menu_ic_focus" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.wm.shell.common.TvWindowMenuActionButton
+ android:id="@+id/tv_split_side_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.wm.shell.splitscreen.tv.TvSplitMenuView>
diff --git a/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml
new file mode 100644
index 000000000000..b2ac85b018be
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+<!-- Layout for TvWindowMenuActionButton -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button"
+ android:layout_width="@dimen/tv_window_menu_button_size"
+ android:layout_height="@dimen/tv_window_menu_button_size"
+ android:padding="@dimen/tv_window_menu_button_margin"
+ android:duplicateParentState="true"
+ android:stateListAnimator="@animator/tv_window_menu_action_button_animator"
+ android:focusable="true">
+
+ <View android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:background="@drawable/tv_window_button_bg"/>
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/tv_window_menu_icon_size"
+ android:layout_height="@dimen/tv_window_menu_icon_size"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:tint="@color/tv_window_menu_icon" />
+</FrameLayout> \ 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 2476f65c7e5b..904ae860d76d 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellings"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Gaan by verdeelde skerm in"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Kieslys"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Prent-in-prent-kieslys"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"As jy nie wil hê dat <xliff:g id="NAME">%s</xliff:g> hierdie kenmerk moet gebruik nie, tik om instellings oop te maak en skakel dit af."</string>
<string name="pip_play" msgid="3496151081459417097">"Speel"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Hierdie app kan net in 1 venster oopgemaak word."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Program steun nie begin op sekondêre skerms nie."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skermverdeler"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Skermverdeler"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Volskerm links"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Links 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Links 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tik om hierdie program te herbegin vir ’n beter aansig."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige programme werk beter in portret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van hierdie opsies om jou spasie ten beste te benut"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai jou toestel om dit volskerm te maak"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik langs ’n program om dit te herposisioneer"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Sleep ’n ander program in vir verdeelde skerm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik buite ’n program om dit te herposisioneer"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vou uit vir meer inligting."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimeer"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Maak klein"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Maak toe"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handvatsel"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Rekenaarmodus"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index c87bec093cca..2254fc9beb11 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Beeld-in-beeld"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Maak toe"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
- <string name="pip_move" msgid="1544227837964635439">"Skuif PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Vou PIP uit"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Vou PIP in"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dubbeldruk "<annotation icon="home_icon">" TUIS "</annotation>" vir kontroles"</string>
+ <string name="pip_move" msgid="158770205886688553">"Skuif"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Vou uit"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Vou in"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Dubbeldruk "<annotation icon="home_icon">"TUIS"</annotation>" vir kontroles"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Prent-in-prent-kieslys"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Skuif links"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Skuif regs"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Skuif op"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Skuif af"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klaar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index f0c391cd6b99..8b467041ed5f 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -22,7 +22,8 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ቅንብሮች"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"የተከፈለ ማያ ገጽን አስገባ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ምናሌ"</string>
- <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> በስዕል-ላይ-ስዕል ውስጥ ነው"</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_play" msgid="3496151081459417097">"አጫውት"</string>
<string name="pip_pause" msgid="690688849510295232">"ባለበት አቁም"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</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="703810061635792791">"የተከፈለ የማያ ገጽ ከፋይ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"የተከፈለ የማያ ገጽ ከፋይ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ለተሻለ ዕይታ ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ።"</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="6688664582871779215">"አንዳንድ መተግበሪያዎች በቁም ፎቶ ውስጥ በተሻለ ሁኔታ ይሰራሉ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ቦታዎን በአግባቡ ለመጠቀም ከእነዚህ አማራጮች ውስጥ አንዱን ይሞክሩ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ወደ የሙሉ ገጽ ዕይታ ለመሄድ መሣሪያዎን ያሽከርክሩት"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ቦታውን ለመቀየር ከመተግበሪያው ቀጥሎ ላይ ሁለቴ መታ ያድርጉ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ለተከፈለ ማያ ገጽ ሌላ መተግበሪያ ይጎትቱ"</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="maximize_button_text" msgid="1650859196290301963">"አስፋ"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"አሳንስ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ዝጋ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ተመለስ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"መያዣ"</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="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index d23353858de6..a6be57889a4e 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -17,12 +17,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ስዕል-ላይ-ስዕል"</string>
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ሥዕል-ላይ-ሥዕል"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ዝጋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
- <string name="pip_move" msgid="1544227837964635439">"ፒአይፒ ውሰድ"</string>
- <string name="pip_expand" msgid="7605396312689038178">"ፒአይፒን ዘርጋ"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"ፒአይፒን ሰብስብ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ለመቆጣጠሪያዎች "<annotation icon="home_icon">"መነሻ"</annotation>"ን ሁለቴ ይጫኑ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ውሰድ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ዘርጋ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ሰብስብ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ለመቆጣጠሪያዎች "<annotation icon="home_icon">"መነሻ"</annotation>"ን ሁለቴ ይጫኑ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"የሥዕል-ላይ-ሥዕል ምናሌ።"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ወደ ግራ ውሰድ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ወደ ቀኝ ውሰድ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ወደ ላይ ውሰድ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ወደ ታች ውሰድ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ተጠናቅቋል"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index aa4b3b704110..635334db0d64 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"الإعدادات"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"الدخول في وضع تقسيم الشاشة"</string>
<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_play" msgid="3496151081459417097">"تشغيل"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"لا يمكن فتح هذا التطبيق إلا في نافذة واحدة."</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="703810061635792791">"أداة تقسيم الشاشة"</string>
+ <string name="divider_title" msgid="5482989479865361192">"أداة تقسيم الشاشة"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"عرض النافذة اليسرى بملء الشاشة"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ضبط حجم النافذة اليسرى ليكون ٧٠%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ضبط حجم النافذة اليسرى ليكون ٥٠%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"انقر لإعادة تشغيل هذا التطبيق للحصول على عرض أفضل."</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="6688664582871779215">"تعمل بعض التطبيقات على أكمل وجه في الشاشات العمودية"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"جرِّب تنفيذ أحد هذه الخيارات للاستفادة من مساحتك إلى أقصى حد."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"قم بتدوير الشاشة للانتقال إلى وضع ملء الشاشة."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"انقر مرتين بجانب التطبيق لتغيير موضعه."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</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="maximize_button_text" msgid="1650859196290301963">"تكبير"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"تصغير"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"إغلاق"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"رجوع"</string>
+ <string name="handle_text" msgid="1766582106752184456">"مقبض"</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="more_button_text" msgid="3655388105592893530">"المزيد"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"نافذة عائمة"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
index a1ceda5fc987..82ab8e9ee15b 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"نافذة ضمن النافذة"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"إغلاق"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
- <string name="pip_move" msgid="1544227837964635439">"‏نقل نافذة داخل النافذة (PIP)"</string>
- <string name="pip_expand" msgid="7605396312689038178">"‏توسيع نافذة داخل النافذة (PIP)"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"‏تصغير نافذة داخل النافذة (PIP)"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" انقر مرتين على "<annotation icon="home_icon">" الصفحة الرئيسية "</annotation>" للوصول لعناصر التحكم."</string>
+ <string name="pip_move" msgid="158770205886688553">"نقل"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"توسيع"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"تصغير"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"انقر مرتين على "<annotation icon="home_icon">" الرئيسية "</annotation>" للوصول لعناصر التحكم."</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"قائمة نافذة ضمن النافذة"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"نقل لليسار"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"نقل لليمين"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"نقل للأعلى"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"نقل للأسفل"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"تمّ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 985d3b9b96fd..788fd5c0597a 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিং"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"বিভাজিত স্ক্ৰীন ম’ডলৈ যাওক"</string>
<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_play" msgid="3496151081459417097">"প্লে কৰক"</string>
@@ -33,18 +34,20 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই এপ্‌টো কেৱল ১ খন ৱিণ্ড’ত খুলিব পাৰি।"</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="703810061635792791">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</string>
+ <string name="divider_title" msgid="5482989479865361192">"বিভাজিত স্ক্ৰীনৰ বিভাজক"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
- <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
- <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
- <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীনখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীনখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীনখন ৩০% কৰক"</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">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
- <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
- <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীনখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীনখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীনখন ৩০% কৰক"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড ব্যৱহাৰ কৰা"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"বাহিৰ হ’বলৈ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ ছোৱাইপ কৰক অথবা এপ্‌টোৰ ওপৰত যিকোনো ঠাইত টিপক"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"উন্নত ভিউৰ বাবে এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক।"</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="6688664582871779215">"কিছুমান এপে প’ৰ্ট্ৰেইট ম’ডত বেছি ভালকৈ কাম কৰে"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপোনাৰ spaceৰ পৰা পাৰ্যমানে উপকৃত হ’বলৈ ইয়াৰে এটা বিকল্প চেষ্টা কৰি চাওক"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"পূৰ্ণ স্ক্ৰীনলৈ যাবলৈ আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"এপ্‌টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ কাষত দুবাৰ টিপক"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্‌ টানি আনি এৰক"</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="maximize_button_text" msgid="1650859196290301963">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"মিনিমাইজ কৰক"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"বন্ধ কৰক"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"উভতি যাওক"</string>
+ <string name="handle_text" msgid="1766582106752184456">"হেণ্ডেল"</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="more_button_text" msgid="3655388105592893530">"অধিক"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ওপঙা"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 8d7bd9f6a27e..34eaaea33609 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"চিত্ৰৰ ভিতৰত চিত্ৰ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
- <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
+ <string name="pip_close" msgid="2955969519031223530">"বন্ধ কৰক"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
- <string name="pip_move" msgid="1544227837964635439">"পিপ স্থানান্তৰ কৰক"</string>
- <string name="pip_expand" msgid="7605396312689038178">"পিপ বিস্তাৰ কৰক"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"পিপ সংকোচন কৰক"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" নিয়ন্ত্ৰণৰ বাবে "<annotation icon="home_icon">" গৃহপৃষ্ঠা "</annotation>" বুটামত দুবাৰ হেঁচক"</string>
+ <string name="pip_move" msgid="158770205886688553">"স্থানান্তৰ কৰক"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"বিস্তাৰ কৰক"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"সংকোচন কৰক"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"নিয়ন্ত্ৰণৰ বাবে "<annotation icon="home_icon">"গৃহপৃষ্ঠা"</annotation>" বুটামত দুবাৰ টিপক"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"চিত্ৰৰ ভিতৰৰ চিত্ৰ মেনু।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"বাওঁফাললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"সোঁফাললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ওপৰলৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"তললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"হ’ল"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 8cd9b7a635ab..a56918d98592 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Bölünmüş ekrana daxil olun"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Şəkildə Şəkil Menyusu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> tətbiqinin bu funksiyadan istifadə etməyini istəmirsinizsə, ayarları açmaq və deaktiv etmək üçün klikləyin."</string>
<string name="pip_play" msgid="3496151081459417097">"Oxudun"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu tətbiq yalnız 1 pəncərədə açıla bilər."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcısı"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Bölünmüş ekran ayırıcısı"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Sol tam ekran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sol 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sol 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bəzi tətbiqlər portret rejimində daha yaxşı işləyir"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Məkanınızdan maksimum yararlanmaq üçün bu seçimlərdən birini sınayın"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana keçmək üçün cihazınızı fırladın"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tətbiqin yerini dəyişmək üçün yanına iki dəfə toxunun"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Bölünmüş ekrandan istifadə etmək üçün başqa tətbiqi sürüşdürüb gətirin"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tətbiqin yerini dəyişmək üçün kənarına iki dəfə toxunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ətraflı məlumat üçün genişləndirin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Böyüdün"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Kiçildin"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Bağlayın"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Geriyə"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Hər kəsə açıq istifadəçi adı"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Rejimi"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Ardı"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index 87c46fa41a01..c45a09645075 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Şəkil-içində-Şəkil"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Bağlayın"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP tətbiq edin"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP-ni genişləndirin"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP-ni yığcamlaşdırın"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nizamlayıcılar üçün "<annotation icon="home_icon">" ƏSAS SƏHİFƏ "</annotation>" süçimini iki dəfə basın"</string>
+ <string name="pip_move" msgid="158770205886688553">"Köçürün"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Genişləndirin"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Yığcamlaşdırın"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Nizamlayıcılar üçün "<annotation icon="home_icon">"ƏSAS SƏHİFƏ "</annotation>" seçiminə iki dəfə basın"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Şəkildə şəkil menyusu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola köçürün"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa köçürün"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Yuxarı köçürün"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Aşağı köçürün"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hazırdır"</string>
</resources>
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 49524c608543..dcb03aa0b365 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Podešavanja"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Uđi na podeljeni ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni slike u slici."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da biste otvorili podešavanja i isključili je."</string>
<string name="pip_play" msgid="3496151081459417097">"Pusti"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija može da se otvori samo u jednom prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdelnik podeljenog ekrana"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Razdelnik podeljenog ekrana"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Režim celog ekrana za levi ekran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi ekran 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi ekran 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcionišu u uspravnom režimu"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste na najbolji način iskoristili prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotirajte uređaj za prikaz preko celog ekrana"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste promenili njenu poziciju"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste promenili njenu poziciju"</string>
<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="maximize_button_text" msgid="1650859196290301963">"Uvećajte"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Umanjite"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Režim za računare"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Još"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index c87f30611a07..6dc4ab1cea79 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika u slici"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zatvori"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"Premesti sliku u slici"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Proširi sliku u slici"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Skupi sliku u slici"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">" HOME "</annotation>" za kontrole"</string>
+ <string name="pip_move" msgid="158770205886688553">"Premesti"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Skupi"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Dvaput pritisnite "<annotation icon="home_icon">" HOME "</annotation>" za kontrole"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni Slika u slici."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomerite nalevo"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomerite nadesno"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pomerite nagore"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pomerite nadole"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 1767e0d66241..f6b285a47f5a 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Налады"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Падзяліць экран"</string>
<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_play" msgid="3496151081459417097">"Прайграць"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Гэту праграму можна адкрыць толькі ў адным акне."</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="703810061635792791">"Раздзяляльнік падзеленага экрана"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Раздзяляльнік падзеленага экрана"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Націсніце, каб перазапусціць гэту праграму для лепшага прагляду."</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="6688664582871779215">"Некаторыя праграмы лепш за ўсё працуюць у кніжнай арыентацыі"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Каб эфектыўна выкарыстоўваць прастору, паспрабуйце адзін з гэтых варыянтаў"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Каб перайсці ў поўнаэкранны рэжым, павярніце прыладу"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двойчы націсніце побач з праграмай, каб перамясціць яе"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</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="maximize_button_text" msgid="1650859196290301963">"Разгарнуць"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Згарнуць"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрыць"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Маркер"</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="more_button_text" msgid="3655388105592893530">"Яшчэ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Зрабіць рухомым акном"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index 3566bc372820..20e725f1aa09 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Відарыс у відарысе"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Закрыць"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
- <string name="pip_move" msgid="1544227837964635439">"Перамясціць PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Разгарнуць відарыс у відарысе"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Згарнуць відарыс у відарысе"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Двойчы націсніце "<annotation icon="home_icon">" ГАЛОЎНЫ ЭКРАН "</annotation>" для пераходу ў налады"</string>
+ <string name="pip_move" msgid="158770205886688553">"Перамясціць"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Разгарнуць"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Згарнуць"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Двойчы націсніце "<annotation icon="home_icon">"ГАЛОЎНЫ"</annotation>" для пераходу ў налады"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню рэжыму \"Відарыс у відарысе\"."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Перамясціць улева"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Перамясціць управа"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Перамясціць уверх"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Перамясціць уніз"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Гатова"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index c22fb86a4d4d..044f2a7438dd 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Преминаване към разделен екран"</string>
<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_play" msgid="3496151081459417097">"Пускане"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</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="703810061635792791">"Разделител в режима за разделен екран"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Разделител в режима за разделен екран"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Докоснете, за да рестартирате това приложение с цел по-добър изглед."</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="6688664582871779215">"Някои приложения работят най-добре във вертикален режим"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Изпробвайте една от следните опции, за да се възползвате максимално от мястото на екрана"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Завъртете екрана си, за да преминете в режим на цял екран"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Докоснете два пъти дадено приложение, за да промените позицията му"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</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="maximize_button_text" msgid="1650859196290301963">"Увеличаване"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Намаляване"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затваряне"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Манипулатор"</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="more_button_text" msgid="3655388105592893530">"Още"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Плаващо"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
index 91049fd2cf02..e9906f952455 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Картина в картината"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Затваряне"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
- <string name="pip_move" msgid="1544227837964635439">"„Картина в картина“: Преместв."</string>
- <string name="pip_expand" msgid="7605396312689038178">"Разгъване на прозореца за PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Свиване на прозореца за PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" За достъп до контролите натиснете 2 пъти "<annotation icon="home_icon">"НАЧАЛО"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Преместване"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Разгъване"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Свиване"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"За достъп до контролите натиснете два пъти "<annotation icon="home_icon">"НАЧАЛО"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню за функцията „Картина в картината“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Преместване наляво"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Преместване надясно"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Преместване нагоре"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Преместване надолу"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index c0944e0584e6..1fb0292bc56d 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"সেটিংস"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"\'স্প্লিট স্ক্রিন\' মোড চালু করুন"</string>
<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_play" msgid="3496151081459417097">"চালান"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই অ্যাপটি শুধু ১টি উইন্ডোয় খোলা যেতে পারে।"</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="703810061635792791">"বিভক্ত-স্ক্রিন বিভাজক"</string>
+ <string name="divider_title" msgid="5482989479865361192">"স্প্লিট স্ক্রিন বিভাজক"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাঁ দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"৭০% বাকি আছে"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"৫০% বাকি আছে"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন।"</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="6688664582871779215">"কিছু অ্যাপ \'পোর্ট্রেট\' মোডে সবচেয়ে ভাল কাজ করে"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপনার স্পেস সবচেয়ে ভালভাবে কাজে লাগাতে এইসব বিকল্পের মধ্যে কোনও একটি ব্যবহার করে দেখুন"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"\'ফুল স্ক্রিন\' মোডে যেতে ডিভাইস ঘোরান"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"কোনও অ্যাপের পাশে ডবল ট্যাপ করে সেটির জায়গা পরিবর্তন করুন"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"স্প্লিট স্ক্রিনের জন্য অন্য অ্যাপে টেনে আনুন"</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="maximize_button_text" msgid="1650859196290301963">"বড় করুন"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ছোট করুন"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"বন্ধ করুন"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ফিরে যান"</string>
+ <string name="handle_text" msgid="1766582106752184456">"হাতল"</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="more_button_text" msgid="3655388105592893530">"আরও"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ফ্লোট"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
index 792708d128a5..b515154ec3e0 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ছবির-মধ্যে-ছবি"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
+ <string name="pip_close" msgid="2955969519031223530">"বন্ধ করুন"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP সরান"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP বড় করুন"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP আড়াল করুন"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" কন্ট্রোলের জন্য "<annotation icon="home_icon">" হোম "</annotation>" বোতামে ডবল প্রেস করুন"</string>
+ <string name="pip_move" msgid="158770205886688553">"সরান"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"বড় করুন"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"আড়াল করুন"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"কন্ট্রোলের জন্য "<annotation icon="home_icon">" হোম "</annotation>" বোতামে ডবল প্রেস করুন"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ছবির-মধ্যে-ছবি মেনু।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"বাঁদিকে সরান"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ডানদিকে সরান"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"উপরে তুলুন"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"নিচে নামান"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"হয়ে গেছে"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index ae01c641cc43..8e52d78f2f5f 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Otvori podijeljeni ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni načina rada slike u slici"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da <xliff:g id="NAME">%s</xliff:g> koristi ovu funkciju, dodirnite da otvorite postavke i isključite je."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija se može otvoriti samo u 1 prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
- <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik ekrana"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog ekrana"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Razdjelnik podijeljenog ekrana"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevo cijeli ekran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevo 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevo 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Određene aplikacije najbolje funkcioniraju u uspravnom načinu rada"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da maksimalno iskoristite prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zarotirajte uređaj da aktivirate prikaz preko cijelog ekrana"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da promijenite njen položaj"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da promijenite njen položaj"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za više informacija."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziranje"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimiziranje"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvaranje"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Način rada radne površine"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
index b7f0dca1b5a5..99e076b31180 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika u slici"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zatvori sliku u slici"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zatvori"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"Pokreni sliku u slici"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Proširi sliku u slici"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Suzi sliku u slici"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">" POČETNI EKRAN "</annotation>" za kontrole"</string>
+ <string name="pip_move" msgid="158770205886688553">"Premjesti"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Suzi"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Dvaput pritisnite "<annotation icon="home_icon">"POČETNI EKRAN"</annotation>" za kontrole"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni za način rada slika u slici."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomjeranje ulijevo"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomjeranje udesno"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pomjeranje nagore"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pomjeranje nadolje"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 8a522b3e6397..6bc4f99ee7ef 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuració"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Entra al mode de pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de pantalla en pantalla"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Si no vols que <xliff:g id="NAME">%s</xliff:g> utilitzi aquesta funció, toca per obrir la configuració i desactiva-la."</string>
<string name="pip_play" msgid="3496151081459417097">"Reprodueix"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aquesta aplicació només pot obrir-se en 1 finestra."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalles"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Separador de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla esquerra completa"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pantalla esquerra al 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pantalla esquerra al 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunes aplicacions funcionen millor en posició vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una d\'aquestes opcions per treure el màxim profit de l\'espai"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositiu per passar a pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Fes doble toc al costat d\'una aplicació per canviar-ne la posició"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Fes doble toc fora d\'una aplicació per canviar-ne la posició"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Desplega per obtenir més informació."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximitza"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimitza"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tanca"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Enrere"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Ansa"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mode d\'escriptori"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Més"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotant"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index 1c560c7afa06..e261db9042bd 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantalla en pantalla"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Tanca"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mou pantalla en pantalla"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Desplega pantalla en pantalla"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Replega pantalla en pantalla"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Prem dos cops "<annotation icon="home_icon">" INICI "</annotation>" per accedir als controls"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mou"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Desplega"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Replega"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Prem dos cops "<annotation icon="home_icon">"INICI"</annotation>" per accedir als controls"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla en pantalla."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mou cap a l\'esquerra"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mou cap a la dreta"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mou cap amunt"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mou cap avall"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Fet"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index d0cf80aef38c..b638b0e5179f 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavení"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Aktivovat rozdělenou obrazovku"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Nabídka"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Nabídka režimu obrazu v obraze"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Pokud nechcete, aby aplikace <xliff:g id="NAME">%s</xliff:g> tuto funkci používala, klepnutím otevřete nastavení a funkci vypněte."</string>
<string name="pip_play" msgid="3496151081459417097">"Přehrát"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tuto aplikaci lze otevřít jen na jednom okně."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Čára rozdělující obrazovku"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Čára rozdělující obrazovku"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levá část na celou obrazovku"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % vlevo"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % vlevo"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Některé aplikace fungují nejlépe na výšku"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pokud chcete maximálně využít prostor, vyzkoušejte jednu z těchto možností"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zařízení přejděte do režimu celé obrazovky"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedle aplikace změňte její umístění"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikaci změníte její umístění"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozbalením zobrazíte další informace."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovat"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimalizovat"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zavřít"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Zpět"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Úchyt"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Režim počítače"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Rozdělená obrazovka"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Více"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
index 9a8cc2b4d70e..72e1ae907cfb 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Obraz v obraze"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Bez názvu)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zavřít"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
- <string name="pip_move" msgid="1544227837964635439">"Přesunout PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Rozbalit PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Sbalit PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ovládací prvky zobrazíte dvojitým stisknutím "<annotation icon="home_icon">"tlačítka plochy"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Přesunout"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Rozbalit"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Sbalit"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Ovládací prvky zobrazíte dvojitým stisknutím "<annotation icon="home_icon">"HOME"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Nabídka režimu obrazu v obraze"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Přesunout doleva"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Přesunout doprava"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Přesunout nahoru"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Přesunout dolů"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index bb81c10c6e1b..e0b7a8cb44d7 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Indstillinger"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Åbn opdelt skærm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu for integreret billede"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> vises som integreret billede"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Hvis du ikke ønsker, at <xliff:g id="NAME">%s</xliff:g> skal benytte denne funktion, kan du åbne indstillingerne og deaktivere den."</string>
<string name="pip_play" msgid="3496151081459417097">"Afspil"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne app kan kun åbnes i 1 vindue."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Adskiller til opdelt skærm"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Adskiller til opdelt skærm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vis venstre del i fuld skærm"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Venstre 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Venstre 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tryk for at genstarte denne app, så visningen forbedres."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nogle apps fungerer bedst i stående format"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv én af disse muligheder for at få mest muligt ud af dit rum"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Drej din enhed for at gå til fuld skærm"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryk to gange ud for en app for at ændre dens placering"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Træk en anden app hertil for at bruge opdelt skærm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryk to gange uden for en app for at justere dens placering"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Udvid for at få flere oplysninger."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimér"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Luk"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Tilbage"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Håndtag"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Computertilstand"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Opdelt skærm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mere"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Svævende"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
index cba660ac723c..5881b0674ad2 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Integreret billede"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uden titel)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Luk"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
- <string name="pip_move" msgid="1544227837964635439">"Flyt PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Udvid PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Skjul PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tryk to gange på "<annotation icon="home_icon">" HJEM "</annotation>" for at se indstillinger"</string>
+ <string name="pip_move" msgid="158770205886688553">"Flyt"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Udvid"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Skjul"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Tryk to gange på "<annotation icon="home_icon">"HJEM"</annotation>" for at se indstillinger"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu for integreret billede."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flyt til venstre"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flyt til højre"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Flyt op"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Flyt ned"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Udfør"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index c5d945a982ef..caca8b42154e 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"„Geteilter Bildschirm“ 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>
<string name="pip_notification_message" msgid="8854051911700302620">"Wenn du nicht möchtest, dass <xliff:g id="NAME">%s</xliff:g> diese Funktion verwendet, tippe, um die Einstellungen zu öffnen und die Funktion zu deaktivieren."</string>
<string name="pip_play" msgid="3496151081459417097">"Wiedergeben"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen im Modus für geteilten Bildschirm nicht."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms 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>
<string name="accessibility_divider" msgid="703810061635792791">"Bildschirmteiler"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Bildschirmteiler"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vollbild links"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % links"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % links"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tippe, um diese App neu zu starten und die Ansicht zu verbessern."</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="6688664582871779215">"Einige Apps funktionieren am besten im Hochformat"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Mithilfe dieser Möglichkeiten kannst du dein Display optimal nutzen"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gerät drehen, um zum Vollbildmodus zu wechseln"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Neben einer App doppeltippen, um die Position zu ändern"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Weitere App hineinziehen, um den Bildschirm zu teilen"</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>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximieren"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimieren"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Zurück"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Ziehpunkt"</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="more_button_text" msgid="3655388105592893530">"Mehr"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 02a1b66eb63f..5c5cbda296a1 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bild im Bild"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Schließen"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
- <string name="pip_move" msgid="1544227837964635439">"BiB verschieben"</string>
- <string name="pip_expand" msgid="7605396312689038178">"BiB maximieren"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"BiB minimieren"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Für Steuerelemente zweimal "<annotation icon="home_icon">"STARTBILDSCHIRMTASTE"</annotation>" drücken"</string>
+ <string name="pip_move" msgid="158770205886688553">"Bewegen"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Maximieren"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Minimieren"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Für Steuerelemente 2× "<annotation icon="home_icon">"STARTBILDSCHIRMTASTE"</annotation>" drücken"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menü „Bild im Bild“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Nach links bewegen"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Nach rechts bewegen"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Nach oben bewegen"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Nach unten bewegen"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Fertig"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 70f55058925c..ffb4fb0169a9 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ρυθμίσεις"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Μετάβαση σε διαχωρισμό οθόνης"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Μενού"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Μενού λειτουργίας Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Η λειτουργία picture-in-picture είναι ενεργή σε <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>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</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="703810061635792791">"Διαχωριστικό οθόνης"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Διαχωριστικό οθόνης"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή."</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="6688664582871779215">"Ορισμένες εφαρμογές λειτουργούν καλύτερα σε κατακόρυφο προσανατολισμό"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Δοκιμάστε μία από αυτές τις επιλογές για να αξιοποιήσετε στο έπακρο τον χώρο σας."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Περιστρέψτε τη συσκευή σας για μετάβαση σε πλήρη οθόνη."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Πατήστε δύο φορές δίπλα σε μια εφαρμογή για να αλλάξετε τη θέση της."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης"</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="maximize_button_text" msgid="1650859196290301963">"Μεγιστοποίηση"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Ελαχιστοποίηση"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Κλείσιμο"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Πίσω"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Λαβή"</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="more_button_text" msgid="3655388105592893530">"Περισσότερα"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
index 24cd030cd754..a80e2c72de7e 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Κλείσιμο"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
- <string name="pip_move" msgid="1544227837964635439">"Μετακίνηση PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Ανάπτυξη PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Σύμπτυξη PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Πατήστε δύο φορές "<annotation icon="home_icon">" ΑΡΧΙΚΗ ΟΘΟΝΗ "</annotation>" για στοιχεία ελέγχου"</string>
+ <string name="pip_move" msgid="158770205886688553">"Μετακίνηση"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Ανάπτυξη"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Σύμπτυξη"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Πατ. δύο φορές το κουμπί "<annotation icon="home_icon">"αρχ. οθ."</annotation>" για στ. ελέγχου"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Μενού λειτουργίας Picture-in-Picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Μετακίνηση αριστερά"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Μετακίνηση δεξιά"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Μετακίνηση επάνω"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Μετακίνηση κάτω"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Τέλος"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 0b5aefa5c72e..c71011d3b359 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
<string name="pip_play" msgid="3496151081459417097">"Play"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
index 82257b42814d..71d02271090d 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Close"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <string name="pip_move" msgid="158770205886688553">"Move"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Double-press "<annotation icon="home_icon">"HOME"</annotation>" for controls"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Move up"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Move down"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Done"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 0b5aefa5c72e..05091fb11864 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-Picture Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
<string name="pip_play" msgid="3496151081459417097">"Play"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in 1 window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
- <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split-screen divider"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Split-screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
@@ -64,21 +67,31 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles anytime"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
- <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <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="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="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximize"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimize"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
index 82257b42814d..09def6b69f06 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -17,12 +17,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Close"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <string name="pip_move" msgid="158770205886688553">"Move"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Double press "<annotation icon="home_icon">"HOME"</annotation>" for controls"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-Picture menu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Move up"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Move down"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Done"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 0b5aefa5c72e..c71011d3b359 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
<string name="pip_play" msgid="3496151081459417097">"Play"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
index 82257b42814d..71d02271090d 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Close"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <string name="pip_move" msgid="158770205886688553">"Move"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Double-press "<annotation icon="home_icon">"HOME"</annotation>" for controls"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Move up"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Move down"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Done"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 0b5aefa5c72e..c71011d3b359 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"If you don\'t want <xliff:g id="NAME">%s</xliff:g> to use this feature, tap to open settings and turn it off."</string>
<string name="pip_play" msgid="3496151081459417097">"Play"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Drag in another app for split-screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"More"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
index 82257b42814d..71d02271090d 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Close"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Double-press "<annotation icon="home_icon">" HOME "</annotation>" for controls"</string>
+ <string name="pip_move" msgid="158770205886688553">"Move"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Collapse"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Double-press "<annotation icon="home_icon">"HOME"</annotation>" for controls"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Picture-in-picture menu"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Move left"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Move right"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Move up"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Move down"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Done"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 5c3d0f65374a..2993fe7b81f4 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎Settings‎‏‎‎‏‎"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎Enter split screen‎‏‎‎‏‎"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎Menu‎‏‎‎‏‎"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎Picture-in-Picture Menu‎‏‎‎‏‎"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ is in picture-in-picture‎‏‎‎‏‎"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎If you don\'t want ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ to use this feature, tap to open settings and turn it off.‎‏‎‎‏‎"</string>
<string name="pip_play" msgid="3496151081459417097">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎Play‎‏‎‎‏‎"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎App may not work with split-screen.‎‏‎‎‏‎"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎App does not support split-screen.‎‏‎‎‏‎"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎This app can only be opened in 1 window.‎‏‎‎‏‎"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎App does not support launch on secondary displays.‎‏‎‎‏‎"</string>
<string name="accessibility_divider" msgid="703810061635792791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎Split-screen divider‎‏‎‎‏‎"</string>
+ <string name="divider_title" msgid="5482989479865361192">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎Split-screen divider‎‏‎‎‏‎"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎Left full screen‎‏‎‎‏‎"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎Left 70%‎‏‎‎‏‎"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎Left 50%‎‏‎‎‏‎"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎Tap to restart this app for a better view.‎‏‎‎‏‎"</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎Some apps work best in portrait‎‏‎‎‏‎"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎Try one of these options to make the most of your space‎‏‎‎‏‎"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎Rotate your device to go full screen‎‏‎‎‏‎"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎Double-tap next to an app to reposition it‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎See and do more‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎Drag in another app for split-screen‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‏‎Double-tap outside an app to reposition it‎‏‎‎‏‎"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎Got it‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎Expand for more information.‎‏‎‎‏‎"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎Maximize‎‏‎‎‏‎"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‎Minimize‎‏‎‎‏‎"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎Close‎‏‎‎‏‎"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎Back‎‏‎‎‏‎"</string>
+ <string name="handle_text" msgid="1766582106752184456">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎Handle‎‏‎‎‏‎"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎Fullscreen‎‏‎‎‏‎"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎Desktop Mode‎‏‎‎‏‎"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎Split Screen‎‏‎‎‏‎"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎More‎‏‎‎‏‎"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎Float‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
index a6e494cfed3c..405770166274 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎Picture-in-Picture‎‏‎‎‏‎"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎(No title program)‎‏‎‎‏‎"</string>
- <string name="pip_close" msgid="9135220303720555525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎Close PIP‎‏‎‎‏‎"</string>
+ <string name="pip_close" msgid="2955969519031223530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎Close‎‏‎‎‏‎"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎Full screen‎‏‎‎‏‎"</string>
- <string name="pip_move" msgid="1544227837964635439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎Move PIP‎‏‎‎‏‎"</string>
- <string name="pip_expand" msgid="7605396312689038178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎Expand PIP‎‏‎‎‏‎"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎Collapse PIP‎‏‎‎‏‎"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎ Double press ‎‏‎‎‏‏‎"<annotation icon="home_icon">"‎‏‎‎‏‏‏‎ HOME ‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎ for controls‎‏‎‎‏‎"</string>
+ <string name="pip_move" msgid="158770205886688553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎Move‎‏‎‎‏‎"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎Expand‎‏‎‎‏‎"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎Collapse‎‏‎‎‏‎"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎Double press ‎‏‎‎‏‏‎"<annotation icon="home_icon">"‎‏‎‎‏‏‏‎HOME‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎ for controls‎‏‎‎‏‎"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎Picture-in-Picture menu.‎‏‎‎‏‎"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‎Move left‎‏‎‎‏‎"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎Move right‎‏‎‎‏‎"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎Move up‎‏‎‎‏‎"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎Move down‎‏‎‎‏‎"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎Done‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index e523ae53b0cc..0eaca8ba040f 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Introducir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de pantalla en pantalla"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en modo de Pantalla en pantalla"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> use esta función, presiona para abrir la configuración y desactivarla."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app solo puede estar abierta en 1 ventana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda: 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda: 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Presiona para reiniciar esta app y tener una mejor vista."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas apps funcionan mejor en modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba estas opciones para aprovechar al máximo tu espacio"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rota el dispositivo para ver la pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Presiona dos veces junto a una app para cambiar su posición"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra otra app para el modo de pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Presiona dos veces fuera de una app para cambiar su ubicación"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expande para obtener más información."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index 458f6b15b857..e0f3297ff966 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantalla en pantalla"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Cerrar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Maximizar PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Minimizar PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Presiona dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Presiona dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla en pantalla"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover hacia la derecha"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover hacia arriba"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover hacia abajo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Listo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 974960708190..9c8fed17859b 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ajustes"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Introducir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de imagen en imagen"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en imagen en imagen"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Si no quieres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca la notificación para abrir los ajustes y desactivarla."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación solo puede abrirse en una ventana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Dividir la pantalla"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda 50%"</string>
@@ -46,10 +49,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar modo Una mano"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo Una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo Una mano"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -63,7 +66,7 @@
<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>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamados \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toca para reiniciar esta aplicación y obtener una mejor vista."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas aplicaciones funcionan mejor en vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba una de estas opciones para sacar el máximo partido al espacio de tu pantalla"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositivo para ir al modo de pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dos veces junto a una aplicación para cambiar su posición"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra otra aplicación para activar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dos veces fuera de una aplicación para cambiarla de posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mostrar más información"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo Escritorio"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index 0a690984dac5..38be3effe356 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Imagen en imagen"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sin título)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Cerrar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover imagen en imagen"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Mostrar imagen en imagen"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Ocultar imagen en imagen"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pulsa dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Mostrar"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Pulsa dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de imagen en imagen."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover hacia la derecha"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover hacia arriba"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover hacia abajo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hecho"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index a5f82a6452c4..e8cbe5387410 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Seaded"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Ava jagatud ekraanikuva"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menüü"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menüü Pilt pildis"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Kui te ei soovi, et rakendus <xliff:g id="NAME">%s</xliff:g> seda funktsiooni kasutaks, puudutage seadete avamiseks ja lülitage see välja."</string>
<string name="pip_play" msgid="3496151081459417097">"Esita"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Selle rakenduse saab avada ainult ühes aknas."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekraanijagaja"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Ekraanijagaja"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasak täisekraan"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasak: 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasak: 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Mõni rakendus töötab kõige paremini vertikaalpaigutuses"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proovige ühte neist valikutest, et oma ruumi parimal moel kasutada"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pöörake seadet, et aktiveerida täisekraanirežiim"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Topeltpuudutage rakenduse kõrval, et selle asendit muuta"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Topeltpuudutage rakendusest väljaspool, et selle asendit muuta"</string>
<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="maximize_button_text" msgid="1650859196290301963">"Maksimeeri"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimeeri"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sule"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Tagasi"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Käepide"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Lauaarvuti režiim"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Jagatud ekraanikuva"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Rohkem"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
index dc0232303a70..a93cee51ce07 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pilt pildis"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Sule"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
- <string name="pip_move" msgid="1544227837964635439">"Teisalda PIP-režiimi"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Laienda PIP-akent"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Ahenda PIP-aken"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nuppude nägemiseks vajutage 2 korda nuppu "<annotation icon="home_icon">"AVAKUVA"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Teisalda"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Laienda"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Ahenda"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Nuppude nägemiseks vajutage 2 korda nuppu "<annotation icon="home_icon">"AVAKUVA"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menüü Pilt pildis."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Teisalda vasakule"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Teisalda paremale"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Teisalda üles"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Teisalda alla"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Valmis"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index caa335a96222..4417668657e2 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -22,8 +22,9 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ezarpenak"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Sartu pantaila zatituan"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menua"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Pantaila txiki gainjarriaren menua"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea nahi ez baduzu, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
<string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
<string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Leiho bakar batean ireki daiteke aplikazioa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da abiarazi bigarren mailako pantailatan."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Pantaila-zatitzailea"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Pantaila-zatitzailea"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ezarri ezkerraldea pantaila osoan"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ezarri ezkerraldea % 70en"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ezarri ezkerraldea % 50en"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Hobeto ikusteko, sakatu hau aplikazioa berrabiarazteko."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Aplikazio batzuk orientazio bertikalean funtzionatzen dute hobekien"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pantailako eremuari ahalik eta etekinik handiena ateratzeko, probatu aukera hauetakoren bat"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pantaila osoko modua erabiltzeko, biratu gailua"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren ondoko edozein toki"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Pantaila zatituta ikusteko, arrastatu beste aplikazio bat"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren kanpoaldea"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Informazio gehiago lortzeko, zabaldu hau."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizatu"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizatu"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Itxi"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atzera"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Kontu-izena"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Ordenagailuetarako modua"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitua"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
index bce06da2c66f..4b752fc9d1c4 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantaila txiki gainjarria"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Itxi"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mugitu pantaila txiki gainjarria"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Zabaldu pantaila txiki gainjarria"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Tolestu pantaila txiki gainjarria"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Kontrolatzeko aukerak atzitzeko, sakatu birritan "<annotation icon="home_icon">" HASIERA "</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Mugitu"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Zabaldu"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Tolestu"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Kontrolatzeko aukerak atzitzeko, sakatu birritan "<annotation icon="home_icon">"HASIERA"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Pantaila txiki gainjarriaren menua."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Eraman ezkerrera"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Eraman eskuinera"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Eraman gora"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Eraman behera"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Eginda"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 761fb9ddeb2f..7375faf8b30f 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"تنظیمات"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ورود به حالت «صفحهٔ دونیمه»"</string>
<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_play" msgid="3496151081459417097">"پخش"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"این برنامه فقط در ۱ پنجره می‌تواند باز شود."</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="703810061635792791">"تقسیم‌کننده صفحه"</string>
+ <string name="divider_title" msgid="5482989479865361192">"تقسیم‌کننده صفحهٔ دونیمه"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"تمام‌صفحه چپ"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"٪۷۰ چپ"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"٪۵۰ چپ"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراه‌اندازی شود."</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="6688664582871779215">"برخی‌از برنامه‌ها در حالت عمودی عملکرد بهتری دارند"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"با امتحان کردن یکی از این گزینه‌ها، بیشترین بهره را از فضایتان ببرید"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"برای رفتن به حالت تمام صفحه، دستگاهتان را بچرخانید"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"در کنار برنامه دوضربه بزنید تا جابه‌جا شود"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</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="maximize_button_text" msgid="1650859196290301963">"بزرگ کردن"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"کوچک کردن"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"بستن"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"برگشتن"</string>
+ <string name="handle_text" msgid="1766582106752184456">"دستگیره"</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="more_button_text" msgid="3655388105592893530">"بیشتر"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"شناور"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index ff9a03c6cefb..55394cbdc31a 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر در تصویر"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"بستن"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
- <string name="pip_move" msgid="1544227837964635439">"‏انتقال PIP (تصویر در تصویر)"</string>
- <string name="pip_expand" msgid="7605396312689038178">"گسترده کردن «تصویر در تصویر»"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"جمع کردن «تصویر در تصویر»"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" برای کنترل‌ها، دکمه "<annotation icon="home_icon">"صفحه اصلی"</annotation>" را دوبار فشار دهید"</string>
+ <string name="pip_move" msgid="158770205886688553">"انتقال"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"گسترده کردن"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"جمع کردن"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"برای کنترل‌ها، دکمه "<annotation icon="home_icon">"صفحه اصلی"</annotation>" را دوبار فشار دهید"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"منوی تصویر در تصویر."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"انتقال به‌چپ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"انتقال به‌راست"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"انتقال به‌بالا"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"انتقال به‌پایین"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"تمام"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index c809b4879e71..7729d1c62f30 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Asetukset"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Avaa jaettu näyttö"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Valikko"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Kuva kuvassa ‑valikko"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jos et halua, että <xliff:g id="NAME">%s</xliff:g> voi käyttää tätä ominaisuutta, avaa asetukset napauttamalla ja poista se käytöstä."</string>
<string name="pip_play" msgid="3496151081459417097">"Toista"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tämän sovelluksen voi avata vain yhdessä ikkunassa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Näytön jakaja"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Näytönjakaja"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasen koko näytölle"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasen 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasen 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Osa sovelluksista toimii parhaiten pystytilassa"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Kokeile jotakin näistä vaihtoehdoista, jotta saat parhaan hyödyn näytön tilasta"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Käännä laitetta, niin se siirtyy koko näytön tilaan"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kaksoisnapauta sovellusta, jos haluat siirtää sitä"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kaksoisnapauta sovelluksen ulkopuolella, jos haluat siirtää sitä"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Katso lisätietoja laajentamalla."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Suurenna"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Pienennä"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sulje"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Takaisin"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Kahva"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Työpöytätila"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Jaettu näyttö"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Lisää"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
index 3e8bf9032780..f580d01691f9 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Kuva kuvassa"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Sulje"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
- <string name="pip_move" msgid="1544227837964635439">"Siirrä PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Laajenna PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Tiivistä PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Asetukset: paina "<annotation icon="home_icon">"ALOITUSNÄYTTÖPAINIKETTA"</annotation>" kahdesti"</string>
+ <string name="pip_move" msgid="158770205886688553">"Siirrä"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Laajenna"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Tiivistä"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Asetukset: paina "<annotation icon="home_icon">"ALOITUSNÄYTTÖPAINIKETTA"</annotation>" kahdesti"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Kuva kuvassa ‑valikko."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Siirrä vasemmalle"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Siirrä oikealle"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Siirrä ylös"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Siirrä alas"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Valmis"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 62b2bb65a603..634880072d47 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Entrer dans l\'écran partagé"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu d\'incrustation d\'image"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Si vous ne voulez pas que <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, touchez l\'écran pour ouvrir les paramètres, puis désactivez-la."</string>
<string name="pip_play" msgid="3496151081459417097">"Lire"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette application ne peut être ouverte que dans une fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Séparateur d\'écran partagé"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Plein écran à la gauche"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % à la gauche"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % à la gauche"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applications fonctionnent mieux en mode portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour tirer le meilleur parti de votre espace"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter votre appareil pour passer en plein écran"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Faites glisser une autre application pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Identifiant"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mode Bureau"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flottant"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
index 66e13b89c64b..39a785d4fcc0 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Incrustation d\'image"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Aucun programme de titre)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Fermer"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
- <string name="pip_move" msgid="1544227837964635439">"Déplacer l\'image incrustée"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Développer l\'image incrustée"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Réduire l\'image incrustée"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Appuyez deux fois sur "<annotation icon="home_icon">" ACCUEIL "</annotation>" pour les commandes"</string>
+ <string name="pip_move" msgid="158770205886688553">"Déplacer"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Développer"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Réduire"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Appuyez deux fois sur "<annotation icon="home_icon">"ACCUEIL"</annotation>" pour les commandes"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu d\'incrustation d\'image."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Déplacer vers la gauche"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Déplacer vers la droite"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Déplacer vers le haut"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Déplacer vers le bas"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index b3e22af0a3e3..184221345b23 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Accéder à l\'écran partagé"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu \"Picture-in-picture\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Si vous ne voulez pas que l\'application <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalité, appuyez ici pour ouvrir les paramètres et la désactiver."</string>
<string name="pip_play" msgid="3496151081459417097">"Lecture"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette appli ne peut être ouverte que dans 1 fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Séparateur d\'écran partagé"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Écran de gauche en plein écran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Écran de gauche à 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Écran de gauche à 50 %"</string>
@@ -63,7 +66,7 @@
<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>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou de bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôlez les bulles à tout moment"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Appuyez pour redémarrer cette appli et avoir une meilleure vue."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applis fonctionnent mieux en mode Portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour exploiter pleinement l\'espace"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter l\'appareil pour passer en plein écran"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Appuyez deux fois à côté d\'une appli pour la repositionner"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Appuyez deux fois en dehors d\'une appli pour la repositionner"</string>
<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="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Poignée"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mode ordinateur"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flottante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
index ed9baf5b6215..db4bc54cf665 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programme sans titre)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Fermer mode PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Fermer"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
- <string name="pip_move" msgid="1544227837964635439">"Déplacer le PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Développer la fenêtre PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Réduire la fenêtre PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Menu de commandes : appuyez deux fois sur "<annotation icon="home_icon">"ACCUEIL"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Déplacer"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Développer"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Réduire"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Commandes : appuyez deux fois sur "<annotation icon="home_icon">"ACCUEIL"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu \"Picture-in-picture\"."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Déplacer vers la gauche"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Déplacer vers la droite"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Déplacer vers le haut"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Déplacer vers le bas"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index b8e039602243..2e05d4c3b548 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Inserir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de pantalla superposta"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Se non queres que <xliff:g id="NAME">%s</xliff:g> utilice esta función, toca a configuración para abrir as opcións e desactivar a función."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación só se pode abrir en 1 ventá."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla completa á esquerda"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % á esquerda"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % á esquerda"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunhas aplicacións funcionan mellor en modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proba unha destas opcións para sacar o máximo proveito do espazo da pantalla"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xira o dispositivo para ver o contido en pantalla completa"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dúas veces a carón dunha aplicación para cambiala de posición"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra outra aplicación para usar a pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dúas veces fóra da aplicación para cambiala de posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Despregar para obter máis información."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Pechar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Máis"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
index a057434d7853..22e68d3707ac 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantalla superposta"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sen título)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Pechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover pantalla superposta"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Despregar pantalla superposta"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Contraer pantalla superposta"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Preme "<annotation icon="home_icon">"INICIO"</annotation>" dúas veces para acceder aos controis"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Despregar"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Preme "<annotation icon="home_icon">"INICIO"</annotation>" dúas veces para acceder aos controis"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla superposta."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover cara á esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover cara á dereita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover cara arriba"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover cara abaixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Feito"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index deda2d755e20..e680298c90bc 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"સેટિંગ"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"વિભાજિત સ્ક્રીન મોડમાં દાખલ થાઓ"</string>
<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_play" msgid="3496151081459417097">"ચલાવો"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</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="703810061635792791">"સ્પ્લિટ-સ્ક્રીન વિભાજક"</string>
+ <string name="divider_title" msgid="5482989479865361192">"સ્ક્રીનને વિભાજિત કરતું વિભાજક"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો."</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="6688664582871779215">"અમુક ઍપ પોર્ટ્રેટ મોડમાં શ્રેષ્ઠ રીતે કાર્ય કરે છે"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"તમારી સ્પેસનો વધુને વધુ લાભ લેવા માટે, આ વિકલ્પોમાંથી કોઈ એક અજમાવો"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"પૂર્ણ સ્ક્રીન મોડ લાગુ કરવા માટે, તમારા ડિવાઇસને ફેરવો"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બાજુમાં બે વાર ટૅપ કરો"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"સ્ક્રીન વિભાજન માટે કોઈ અન્ય ઍપમાં ખેંચો"</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="maximize_button_text" msgid="1650859196290301963">"મોટું કરો"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"નાનું કરો"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"બંધ કરો"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"પાછળ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"હૅન્ડલ"</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="more_button_text" msgid="3655388105592893530">"વધુ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ફ્લોટિંગ વિન્ડો"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index d9525910e4c6..01b9b4b987d0 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ચિત્રમાં-ચિત્ર"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
+ <string name="pip_close" msgid="2955969519031223530">"બંધ કરો"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP ખસેડો"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP મોટી કરો"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP નાની કરો"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" નિયંત્રણો માટે "<annotation icon="home_icon">" હોમ "</annotation>" બટન પર બે વાર દબાવો"</string>
+ <string name="pip_move" msgid="158770205886688553">"ખસેડો"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"મોટું કરો"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"નાનું કરો"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"નિયંત્રણો માટે "<annotation icon="home_icon">"હોમ"</annotation>" બટન બે વાર દબાવો"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ચિત્રમાં ચિત્ર મેનૂ."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ડાબે ખસેડો"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"જમણે ખસેડો"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ઉપર ખસેડો"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"નીચે ખસેડો"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"થઈ ગયું"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 36b11514c7e5..9a926d88f7b8 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रीन मोड में जाएं"</string>
<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_play" msgid="3496151081459417097">"चलाएं"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ऐप्लिकेशन शायद स्प्लिट स्क्रीन मोड में काम न करे."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्‍क्रीन का समर्थन नहीं करता है."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"इस ऐप्लिकेशन को सिर्फ़ एक विंडो में खोला जा सकता है."</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="703810061635792791">"विभाजित स्क्रीन विभाजक"</string>
+ <string name="divider_title" msgid="5482989479865361192">"स्प्लिट स्क्रीन डिवाइडर"</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>
@@ -65,20 +68,30 @@
<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_title" msgid="2397251267073294968">"हाल ही के कोई बबल्स नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</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="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"टैप करके ऐप्लिकेशन को रीस्टार्ट करें और बेहतर व्यू पाएं."</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="6688664582871779215">"कुछ ऐप्लिकेशन, पोर्ट्रेट मोड में सबसे अच्छी तरह काम करते हैं"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"जगह का पूरा इस्तेमाल करने के लिए, इनमें से किसी एक विकल्प को आज़माएं"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फ़ुल स्क्रीन मोड में जाने के लिए, डिवाइस को घुमाएं"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बगल में दो बार टैप करें"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट स्क्रीन के लिए, दूसरे ऐप्लिकेशन को खींचें और छोड़ें"</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="maximize_button_text" msgid="1650859196290301963">"बड़ा करें"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"विंडो छोटी करें"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बंद करें"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"वापस जाएं"</string>
+ <string name="handle_text" msgid="1766582106752184456">"हैंडल"</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="more_button_text" msgid="3655388105592893530">"ज़्यादा देखें"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"फ़्लोट"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
index d897ac73f80d..595435bcf8b8 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"पिक्चर में पिक्चर"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
+ <string name="pip_close" msgid="2955969519031223530">"बंद करें"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
- <string name="pip_move" msgid="1544227837964635439">"पीआईपी को दूसरी जगह लेकर जाएं"</string>
- <string name="pip_expand" msgid="7605396312689038178">"पीआईपी विंडो को बड़ा करें"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"पीआईपी विंडो को छोटा करें"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" कंट्रोल मेन्यू पर जाने के लिए, "<annotation icon="home_icon">" होम बटन"</annotation>" दो बार दबाएं"</string>
+ <string name="pip_move" msgid="158770205886688553">"ले जाएं"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"बड़ा करें"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"छोटा करें"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"कंट्रोल मेन्यू पर जाने के लिए "<annotation icon="home_icon">" होम"</annotation>" को दो बार दबाएं"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"पिक्चर में पिक्चर मेन्यू."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"बाईं ओर ले जाएं"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"दाईं ओर ले जाएं"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ऊपर ले जाएं"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"नीचे ले जाएं"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"हो गया"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 5ecc5585a6e9..23a5970d805e 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Otvorite podijeljeni zaslon"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Izbornik"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Izbornik slike u slici"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ako ne želite da aplikacija <xliff:g id="NAME">%s</xliff:g> upotrebljava tu značajku, dodirnite da biste otvorili postavke i isključili je."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova se aplikacija može otvoriti samo u jednom prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog zaslona"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Razdjelnik podijeljenog zaslona"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevi zaslon u cijeli zaslon"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevi zaslon na 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevi zaslon na 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcioniraju u portretnom usmjerenju"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste maksimalno iskoristili prostor"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zakrenite uređaj radi prikaza na cijelom zaslonu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste joj promijenili položaj"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste je premjestili"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite da biste saznali više."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziraj"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimiziraj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvori"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Natrag"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Pokazivač"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Stolni način rada"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
index 8f5f3164c4d7..965b9b8c31c6 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika u slici"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zatvori"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
- <string name="pip_move" msgid="1544227837964635439">"Premjesti PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Proširi PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Sažmi PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dvaput pritisnite "<annotation icon="home_icon">"POČETNI ZASLON"</annotation>" za kontrole"</string>
+ <string name="pip_move" msgid="158770205886688553">"Premjesti"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Proširi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Sažmi"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Dvaput pritisnite "<annotation icon="home_icon">"POČETNI ZASLON"</annotation>" za kontrole"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Izbornik slike u slici."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pomaknite ulijevo"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pomaknite udesno"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pomaknite prema gore"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pomaknite prema dolje"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 2295250e2853..1bbbdb7786b4 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Beállítások"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Váltás osztott képernyőre"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Kép a képben menü"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ha nem szeretné, hogy a(z) <xliff:g id="NAME">%s</xliff:g> használja ezt a funkciót, koppintson a beállítások megnyitásához, és kapcsolja ki."</string>
<string name="pip_play" msgid="3496151081459417097">"Lejátszás"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ez az alkalmazás csak egy ablakban nyitható meg."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Elválasztó az osztott nézetben"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Elválasztó az osztott nézetben"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Bal oldali teljes képernyőre"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Bal oldali 70%-ra"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Bal oldali 50%-ra"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Egyes alkalmazások álló tájolásban működnek a leghatékonyabban"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Próbálja ki az alábbi beállítások egyikét, hogy a legjobban ki tudja használni képernyő területét"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"A teljes képernyős mód elindításához forgassa el az eszközt"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Koppintson duplán az alkalmazás mellett az áthelyezéséhez"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Koppintson duplán az alkalmazáson kívül az áthelyezéséhez"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kibontással további információkhoz juthat."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Teljes méret"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Kis méret"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Bezárás"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Vissza"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Fogópont"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyő"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Asztali üzemmód"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Osztott képernyő"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Továbbiak"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Lebegő"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index fc8d79589121..90cbfe643c82 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Kép a képben"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Bezárás"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP áthelyezése"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Kép a képben kibontása"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Kép a képben összecsukása"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Vezérlők: "<annotation icon="home_icon">" KEZDŐKÉPERNYŐ "</annotation>" gomb kétszer megnyomva"</string>
+ <string name="pip_move" msgid="158770205886688553">"Áthelyezés"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Kibontás"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Összecsukás"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Vezérlők: A "<annotation icon="home_icon">"KEZDŐKÉPERNYŐ"</annotation>" gomb kétszeri megnyomása"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Kép a képben menü."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mozgatás balra"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mozgatás jobbra"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mozgatás felfelé"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mozgatás lefelé"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Kész"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 208936539094..6eef4afda3ff 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Կարգավորումներ"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Մտնել տրոհված էկրանի ռեժիմ"</string>
<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_play" msgid="3496151081459417097">"Նվագարկել"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Այս հավելվածը հնարավոր է բացել միայն մեկ պատուհանում։"</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="703810061635792791">"Տրոհված էկրանի բաժանիչ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Տրոհված էկրանի բաժանիչ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար։"</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="6688664582871779215">"Որոշ հավելվածներ լավագույնս աշխատում են դիմանկարի ռեժիմում"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Փորձեք այս տարբերակներից մեկը՝ տարածքը հնարավորինս արդյունավետ օգտագործելու համար"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Պտտեք սարքը՝ լիաէկրան ռեժիմին անցնելու համար"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</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="maximize_button_text" msgid="1650859196290301963">"Ծավալել"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Ծալել"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Փակել"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Հետ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Նշիչ"</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="more_button_text" msgid="3655388105592893530">"Ավելին"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Լողացող պատուհան"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
index f5665b8dd166..30b5911147b5 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Նկար նկարի մեջ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Փակել"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
- <string name="pip_move" msgid="1544227837964635439">"Տեղափոխել PIP-ը"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Ծավալել PIP-ը"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Ծալել PIP-ը"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Կարգավորումների համար կրկնակի սեղմեք "<annotation icon="home_icon">"ԳԼԽԱՎՈՐ ԷԿՐԱՆ"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Տեղափոխել"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Ծավալել"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Ծալել"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Կարգավորումների համար կրկնակի սեղմեք "<annotation icon="home_icon">"ԳԼԽԱՎՈՐ ԷԿՐԱՆ"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"«Նկար նկարի մեջ» ռեժիմի ընտրացանկ։"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Տեղափոխել ձախ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Տեղափոխել աջ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Տեղափոխել վերև"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Տեղափոխել ներքև"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Պատրաստ է"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 1b46b2fe2570..61a9558972e0 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk ke mode layar terpisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
<string name="pip_play" msgid="3496151081459417097">"Putar"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplikasi ini hanya dapat dibuka di 1 jendela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Pembagi layar terpisah"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Pembagi layar terpisah"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Layar penuh di kiri"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Beberapa aplikasi berfungsi paling baik dalam mode potret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Coba salah satu opsi berikut untuk mengoptimalkan area layar Anda"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar perangkat untuk tampilan layar penuh"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketuk dua kali di samping aplikasi untuk mengubah posisinya"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
+ <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="maximize_button_text" msgid="1650859196290301963">"Maksimalkan"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimalkan"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Tuas"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mode Desktop"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Layar Terpisah"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Lainnya"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
index a1535653f679..0fda69f6c0e4 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tanpa judul)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Tutup"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
- <string name="pip_move" msgid="1544227837964635439">"Pindahkan PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Luaskan PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Ciutkan PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tekan dua kali "<annotation icon="home_icon">" HOME "</annotation>" untuk membuka kontrol"</string>
+ <string name="pip_move" msgid="158770205886688553">"Pindahkan"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Luaskan"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Ciutkan"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Tekan dua kali "<annotation icon="home_icon">"HOME"</annotation>" untuk membuka kontrol"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Picture-in-Picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pindahkan ke kiri"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pindahkan ke kanan"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pindahkan ke atas"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pindahkan ke bawah"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Selesai"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index a201c95137f3..0b873bc82e63 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Stillingar"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Opna skjáskiptingu"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Valmynd"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Valmynd fyrir mynd í mynd"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ef þú vilt ekki að <xliff:g id="NAME">%s</xliff:g> noti þennan eiginleika skaltu ýta til að opna stillingarnar og slökkva á því."</string>
<string name="pip_play" msgid="3496151081459417097">"Spila"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aðeins er hægt að opna þetta forrit í 1 glugga."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skjáskipting"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Skjáskipting"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vinstri á öllum skjánum"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vinstri 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vinstri 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ýta til að endurræsa forritið og fá betri sýn."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sum forrit virka best í skammsniði"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prófaðu einhvern af eftirfarandi valkostum til að nýta plássið sem best"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Snúðu tækinu til að nota allan skjáinn"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ýttu tvisvar við hlið forritsins til að færa það"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ýttu tvisvar utan við forrit til að færa það"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Stækka til að sjá frekari upplýsingar."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Stækka"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minnka"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Loka"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Til baka"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handfang"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Skjáborðsstilling"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Skjáskipting"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Meira"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Reikult"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
index 70ca1afe3aea..e0d604f30d1a 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Mynd í mynd"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Efni án titils)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Loka"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
- <string name="pip_move" msgid="1544227837964635439">"Færa innfellda mynd"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Stækka innfellda mynd"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Minnka innfellda mynd"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ýttu tvisvar á "<annotation icon="home_icon">" HEIM "</annotation>" til að opna stillingar"</string>
+ <string name="pip_move" msgid="158770205886688553">"Færa"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Stækka"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Minnka"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Ýttu tvisvar á "<annotation icon="home_icon">"HEIM"</annotation>" til að opna stillingar"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Valmynd fyrir mynd í mynd."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Færa til vinstri"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Færa til hægri"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Færa upp"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Færa niður"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Lokið"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 7157ed088d30..da4d0bbe1951 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Impostazioni"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Accedi a schermo diviso"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Picture in picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> è in Picture in picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Se non desideri che l\'app <xliff:g id="NAME">%s</xliff:g> utilizzi questa funzione, tocca per aprire le impostazioni e disattivarla."</string>
<string name="pip_play" msgid="3496151081459417097">"Riproduci"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Questa app può essere aperta soltanto in 1 finestra."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Strumento per schermo diviso"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Strumento per schermo diviso"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Schermata sinistra a schermo intero"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Schermata sinistra al 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Schermata sinistra al 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tocca per riavviare quest\'app per una migliore visualizzazione."</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="6688664582871779215">"Alcune app funzionano in modo ottimale in verticale"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una di queste opzioni per ottimizzare lo spazio a tua disposizione"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ruota il dispositivo per passare alla modalità a schermo intero"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tocca due volte accanto a un\'app per riposizionarla"</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="6206339484068670830">"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_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Ingrandisci"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Riduci a icona"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Chiudi"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Indietro"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modalità desktop"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Schermo diviso"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Altro"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Mobile"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index cda627517872..267f67463917 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture in picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Chiudi"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
- <string name="pip_move" msgid="1544227837964635439">"Sposta PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Espandi PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Comprimi PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Premi due volte "<annotation icon="home_icon">" HOME "</annotation>" per aprire i controlli"</string>
+ <string name="pip_move" msgid="158770205886688553">"Sposta"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Espandi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Comprimi"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Premi due volte "<annotation icon="home_icon">"HOME"</annotation>" per accedere ai controlli"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Picture in picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sposta a sinistra"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sposta a destra"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Sposta su"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Sposta giù"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Fine"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 52a6b0676222..e9a53ddcd1e6 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"כניסה למסך המפוצל"</string>
<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_play" msgid="3496151081459417097">"הפעלה"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ניתן לפתוח את האפליקציה הזו רק בחלון אחד."</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="703810061635792791">"מחלק מסך מפוצל"</string>
+ <string name="divider_title" msgid="5482989479865361192">"מחלק מסך מפוצל"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש."</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="6688664582871779215">"חלק מהאפליקציות פועלות בצורה הטובה ביותר במצב תצוגה לאורך"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"כדי להפיק את המרב משטח המסך, ניתן לנסות את אחת מהאפשרויות האלה"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"מסובבים את המכשיר כדי לעבור לתצוגה במסך מלא"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"מקישים הקשה כפולה ליד אפליקציה כדי למקם אותה מחדש"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך מפוצל"</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="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"מזעור"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"סגירה"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"חזרה"</string>
+ <string name="handle_text" msgid="1766582106752184456">"נקודת אחיזה"</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="more_button_text" msgid="3655388105592893530">"עוד"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"בלונים"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 30ce97b998ca..6b30f5642ad3 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"סגירה"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
- <string name="pip_move" msgid="1544227837964635439">"‏העברת תמונה בתוך תמונה (PIP)"</string>
- <string name="pip_expand" msgid="7605396312689038178">"הרחבת חלון תמונה-בתוך-תמונה"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"כיווץ של חלון תמונה-בתוך-תמונה"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" לחיצה כפולה על "<annotation icon="home_icon">" הלחצן הראשי "</annotation>" תציג את אמצעי הבקרה"</string>
+ <string name="pip_move" msgid="158770205886688553">"העברה"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"הרחבה"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"כיווץ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"לחיצה כפולה על "<annotation icon="home_icon">"בית"</annotation>" תציג את אמצעי הבקרה"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"תפריט \'תמונה בתוך תמונה\'."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"הזזה שמאלה"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"הזזה ימינה"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"הזזה למעלה"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"הזזה למטה"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"סיום"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 5a25c24ba034..2930cc3747b9 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"分割画面に切り替え"</string>
<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>でこの機能を使用しない場合は、タップして設定を開いて OFF にしてください。"</string>
<string name="pip_play" msgid="3496151081459417097">"再生"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</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="703810061635792791">"分割画面の分割線"</string>
+ <string name="divider_title" msgid="5482989479865361192">"分割画面の分割線"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"タップしてこのアプリを再起動すると、表示が適切になります。"</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="6688664582871779215">"アプリによっては縦向きにすると正常に動作します"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"スペースを最大限に活用するには、以下の方法のいずれかをお試しください"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"全画面表示にするにはデバイスを回転させてください"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"位置を変えるにはアプリの横をダブルタップしてください"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"位置を変えるにはアプリの外側をダブルタップしてください"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"開くと詳細が表示されます。"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"閉じる"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"戻る"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ハンドル"</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="more_button_text" msgid="3655388105592893530">"その他"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"フローティング"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index e58e7bf6fabc..2a79e3c651a1 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ピクチャー イン ピクチャー"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
+ <string name="pip_close" msgid="2955969519031223530">"閉じる"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP を移動"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP を開く"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP を閉じる"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" コントロールにアクセス: "<annotation icon="home_icon">" ホーム "</annotation>" を 2 回押します"</string>
+ <string name="pip_move" msgid="158770205886688553">"移動"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"開く"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"閉じる"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"コントロールにアクセス: "<annotation icon="home_icon">" ホーム "</annotation>" を 2 回押します"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ピクチャー イン ピクチャーのメニューです。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左に移動"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右に移動"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"上に移動"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"下に移動"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index bff86fa6ffe2..848be3f86392 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"პარამეტრები"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"გაყოფილ ეკრანში შესვლა"</string>
<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_play" msgid="3496151081459417097">"დაკვრა"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</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="703810061635792791">"გაყოფილი ეკრანის რეჟიმის გამყოფი"</string>
+ <string name="divider_title" msgid="5482989479865361192">"ეკრანის გაყოფის გამყოფი"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"შეეხეთ, რომ გადატვირთოთ ეს აპი უკეთესი ხედისთვის."</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="6688664582871779215">"ზოგიერთი აპი უკეთ მუშაობს პორტრეტის რეჟიმში"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"გამოცადეთ ამ ვარიანტებიდან ერთ-ერთი, რათა მაქსიმალურად ისარგებლოთ თქვენი მეხსიერებით"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"მოატრიალეთ თქვენი მოწყობილობა სრული ეკრანის გასაშლელად"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ორმაგად შეეხეთ აპის გვერდითა სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</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="maximize_button_text" msgid="1650859196290301963">"მაქსიმალურად გაშლა"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ჩაკეცვა"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"დახურვა"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"უკან"</string>
+ <string name="handle_text" msgid="1766582106752184456">"იდენტიფიკატორი"</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="more_button_text" msgid="3655388105592893530">"სხვა"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ფარფატი"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index b09686646c8b..58bae02120ff 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ეკრანი ეკრანში"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
+ <string name="pip_close" msgid="2955969519031223530">"დახურვა"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP გადატანა"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP-ის გაშლა"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP-ის ჩაკეცვა"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" მართვის საშუალებებზე წვდომისთვის ორმაგად დააჭირეთ "<annotation icon="home_icon">" მთავარ ღილაკს "</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"გადაადგილება"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"გაშლა"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ჩაკეცვა"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"სახლის მართვის საშუალებებზე წვდომისთვის ორმაგად დააჭირეთ "<annotation icon="home_icon">" მთავარ ღილაკს "</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"მენიუ „ეკრანი ეკრანში“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"მარცხნივ გადატანა"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"მარჯვნივ გადატანა"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ზემოთ გადატანა"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ქვემოთ გადატანა"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"მზადაა"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index f57f3f581c85..8d08ccabb623 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Бөлінген экранға кіру"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Mәзір"</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_play" msgid="3496151081459417097">"Ойнату"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</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="703810061635792791">"Бөлінген экран бөлгіші"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Бөлінген экран бөлгіші"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ыңғайлы көріністі реттеу үшін қолданбаны түртіп, өшіріп қосыңыз."</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="6688664582871779215">"Кейбір қолданба портреттік режимде жақсы жұмыс істейді"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Экранды тиімді пайдалану үшін мына опциялардың бірін байқап көріңіз."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толық экранға ауысу үшін құрылғыңызды бұрыңыз."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Қолданбаның орнын ауыстыру үшін жанынан екі рет түртіңіз."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Экранды бөлу үшін басқа қолданбаға сүйреңіз."</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="maximize_button_text" msgid="1650859196290301963">"Жаю"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Кішірейту"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Жабу"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Артқа"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Идентификатор"</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="more_button_text" msgid="3655388105592893530">"Қосымша"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index 7bade0dff0d9..df5f6171b11b 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Суреттегі сурет"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Жабу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP клипін жылжыту"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP терезесін жаю"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP терезесін жию"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Басқару элементтері: "<annotation icon="home_icon">" НЕГІЗГІ ЭКРАН "</annotation>" түймесін екі рет басыңыз."</string>
+ <string name="pip_move" msgid="158770205886688553">"Жылжыту"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Жаю"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Жию"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Басқару: "<annotation icon="home_icon">" НЕГІЗГІ ЭКРАН "</annotation>" түймесін екі рет басыңыз."</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"Сурет ішіндегі сурет\" мәзірі."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Солға жылжыту"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Оңға жылжыту"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Жоғары жылжыту"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Төмен жылжыту"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Дайын"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 5c04f881fe0e..7c4ea57e81d8 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ការកំណត់"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ចូលមុខងារ​បំបែកអេក្រង់"</string>
<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_play" msgid="3496151081459417097">"លេង"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធី​អាចនឹងមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ។"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</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="703810061635792791">"កម្មវិធីចែកអេក្រង់បំបែក"</string>
+ <string name="divider_title" msgid="5482989479865361192">"បន្ទាត់ខណ្ឌចែកអេក្រង់បំបែក"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ។"</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="6688664582871779215">"កម្មវិធីមួយចំនួនដំណើរការបានប្រសើរបំផុតក្នុងទិសដៅបញ្ឈរ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"សាកល្បងជម្រើសមួយក្នុងចំណោមទាំងនេះ ដើម្បីទទួលបានអត្ថប្រយោជន៍ច្រើនបំផុតពីកន្លែងទំនេររបស់អ្នក"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"បង្វិលឧបករណ៍របស់អ្នក ដើម្បីចូលប្រើអេក្រង់ពេញ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ចុចពីរដងនៅជាប់កម្មវិធីណាមួយ ដើម្បីប្ដូរទីតាំងកម្មវិធីនោះ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"អូស​កម្មវិធី​មួយ​ទៀត​ចូល ដើម្បី​ប្រើ​មុខងារ​បំបែកអេក្រង់"</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="maximize_button_text" msgid="1650859196290301963">"ពង្រីក"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"បង្រួម"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"បិទ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ថយក្រោយ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ឈ្មោះអ្នកប្រើប្រាស់"</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="more_button_text" msgid="3655388105592893530">"ច្រើនទៀត"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"អណ្ដែត"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
index 721be1fc1650..a3c7e22f268a 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"រូបក្នុងរូប"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
- <string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"បិទ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
- <string name="pip_move" msgid="1544227837964635439">"ផ្លាស់ទី PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"ពង្រីក PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"បង្រួម PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ចុចពីរដងលើ"<annotation icon="home_icon">"ប៊ូតុងដើម"</annotation>" ដើម្បីបើកផ្ទាំងគ្រប់គ្រង"</string>
+ <string name="pip_move" msgid="158770205886688553">"ផ្លាស់ទី"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ពង្រីក"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"បង្រួម"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ចុចពីរដងលើ"<annotation icon="home_icon">"ប៊ូតុងដើម"</annotation>" ដើម្បីបើកផ្ទាំងគ្រប់គ្រង"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ម៉ឺនុយ​រូប​ក្នុងរូប"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ផ្លាស់ទី​ទៅ​ឆ្វេង"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ផ្លាស់ទីទៅ​ស្តាំ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ផ្លាស់ទី​ឡើង​លើ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ផ្លាស់ទី​ចុះ​ក្រោម"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"រួចរាល់"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index e91383caa009..72906174a65c 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ಸ್ಪ್ಲಿಟ್‌-ಸ್ಕ್ರೀನ್‌ಗೆ ಪ್ರವೇಶಿಸಿ"</string>
<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_play" msgid="3496151081459417097">"ಪ್ಲೇ"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</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="703810061635792791">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</string>
+ <string name="divider_title" msgid="5482989479865361192">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</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="6688664582871779215">"ಕೆಲವು ಆ್ಯಪ್‌ಗಳು ಪೋರ್ಟ್ರೇಟ್ ಮೋಡ್‌ನಲ್ಲಿ ಅತ್ಯುತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತವೆ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ನಿಮ್ಮ ಸ್ಥಳಾವಕಾಶದ ಅತಿಹೆಚ್ಚು ಪ್ರಯೋಜನ ಪಡೆಯಲು ಈ ಆಯ್ಕೆಗಳಲ್ಲಿ ಒಂದನ್ನು ಬಳಸಿ ನೋಡಿ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಪಕ್ಕದಲ್ಲಿ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ನಲ್ಲಿ ಎಳೆಯಿರಿ"</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="maximize_button_text" msgid="1650859196290301963">"ಹಿಗ್ಗಿಸಿ"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ಕುಗ್ಗಿಸಿ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ಮುಚ್ಚಿರಿ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ಹಿಂದಕ್ಕೆ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ಹ್ಯಾಂಡಲ್"</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="more_button_text" msgid="3655388105592893530">"ಇನ್ನಷ್ಟು"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ಫ್ಲೋಟ್"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 8310c8a1169c..3dfe573a6506 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ಮುಚ್ಚಿರಿ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP ಅನ್ನು ಸರಿಸಿ"</string>
- <string name="pip_expand" msgid="7605396312689038178">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವನ್ನು ವಿಸ್ತರಿಸಿ"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವನ್ನು ಕುಗ್ಗಿಸಿ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ "<annotation icon="home_icon">" ಹೋಮ್ "</annotation>" ಅನ್ನು ಎರಡು ಬಾರಿ ಒತ್ತಿ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ಸರಿಸಿ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ "<annotation icon="home_icon">"ಹೋಮ್"</annotation>" ಅನ್ನು ಎರಡು ಬಾರಿ ಒತ್ತಿರಿ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ ಮೆನು."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ಮೇಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ಕೆಳಗೆ ಸರಿಸಿ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ಮುಗಿದಿದೆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 104ba3f22c96..59b405ff1e72 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"설정"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"화면 분할 모드로 전환"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"메뉴"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"PIP 모드 메뉴"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>에서 이 기능이 사용되는 것을 원하지 않는 경우 탭하여 설정을 열고 기능을 사용 중지하세요."</string>
<string name="pip_play" msgid="3496151081459417097">"재생"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</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="703810061635792791">"화면 분할기"</string>
+ <string name="divider_title" msgid="5482989479865361192">"화면 분할기"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"보기를 개선하려면 탭하여 앱을 다시 시작합니다."</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="6688664582871779215">"일부 앱은 세로 모드에서 가장 잘 작동함"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"공간을 최대한 이용할 수 있도록 이 옵션 중 하나를 시도해 보세요."</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"전체 화면 모드로 전환하려면 기기를 회전하세요."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"앱 위치를 조정하려면 앱 옆을 두 번 탭하세요."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</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="maximize_button_text" msgid="1650859196290301963">"최대화"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"최소화"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"닫기"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"뒤로"</string>
+ <string name="handle_text" msgid="1766582106752184456">"핸들"</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="more_button_text" msgid="3655388105592893530">"더보기"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"플로팅"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
index a3e055a515a1..969a68d0346e 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"PIP 모드"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(제목 없는 프로그램)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
+ <string name="pip_close" msgid="2955969519031223530">"닫기"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP 이동"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP 펼치기"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP 접기"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 제어 메뉴에 액세스하려면 "<annotation icon="home_icon">" 홈 "</annotation>"을 두 번 누르세요."</string>
+ <string name="pip_move" msgid="158770205886688553">"이동"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"펼치기"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"접기"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"제어 메뉴에 액세스하려면 "<annotation icon="home_icon">"홈"</annotation>"을 두 번 누르세요."</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"PIP 모드 메뉴입니다."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"왼쪽으로 이동"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"오른쪽으로 이동"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"위로 이동"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"아래로 이동"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"완료"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 8203622a33fc..528c51d2966e 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Жөндөөлөр"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Экранды бөлүү режимине өтүү"</string>
<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_play" msgid="3496151081459417097">"Ойнотуу"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</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="703810061635792791">"Экранды бөлгүч"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Экранды бөлгүч"</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>
@@ -56,9 +59,9 @@
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> жана дагы <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> колдонмодон <xliff:g id="NOTIFICATION_TITLE">%1$s</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_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_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз."</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="6688664582871779215">"Айрым колдонмолорду тигинен иштетүү туура болот"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Иш чөйрөсүнүн бардык мүмкүнчүлүктөрүн пайдалануу үчүн бул параметрлердин бирин колдонуп көрүңүз"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толук экран режимине өтүү үчүн түзмөктү буруңуз"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Колдонмонун ракурсун өзгөртүү үчүн анын тушуна эки жолу басыңыз"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</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="maximize_button_text" msgid="1650859196290301963">"Чоңойтуу"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Кичирейтүү"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Жабуу"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Артка"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Маркер"</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="more_button_text" msgid="3655388105592893530">"Дагы"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Калкыма"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
index 887ac52c8e43..68262e521f4a 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Сүрөттөгү сүрөт"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Жабуу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP\'ти жылдыруу"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP\'ти жайып көрсөтүү"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP\'ти жыйыштыруу"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Башкаруу элементтерин ачуу үчүн "<annotation icon="home_icon">" БАШКЫ БЕТ "</annotation>" баскычын эки жолу басыңыз"</string>
+ <string name="pip_move" msgid="158770205886688553">"Жылдыруу"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Жайып көрсөтүү"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Жыйыштыруу"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Башкаруу элементтерин ачуу үчүн "<annotation icon="home_icon">" БАШКЫ БЕТ "</annotation>" баскычын эки жолу басыңыз"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Сүрөт ичиндеги сүрөт менюсу."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Солго жылдыруу"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Оңго жылдыруу"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Жогору жылдыруу"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Төмөн жылдыруу"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Бүттү"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 24396786f9d8..d5ea3cf37c8b 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ການຕັ້ງຄ່າ"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ເຂົ້າການແບ່ງໜ້າຈໍ"</string>
<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_play" msgid="3496151081459417097">"ຫຼິ້ນ"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</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="703810061635792791">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ."</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="6688664582871779215">"ແອັບບາງຢ່າງເຮັດວຽກໄດ້ດີທີ່ສຸດໃນໂໝດລວງຕັ້ງ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ໃຫ້ລອງຕົວເລືອກໃດໜຶ່ງເຫຼົ່ານີ້ເພື່ອໃຊ້ປະໂຫຍດຈາກພື້ນທີ່ຂອງທ່ານໃຫ້ໄດ້ສູງສຸດ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ໝຸນອຸປະກອນຂອງທ່ານເພື່ອໃຊ້ແບບເຕັມຈໍ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ແຕະສອງເທື່ອໃສ່ຖັດຈາກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ລາກແອັບອື່ນເຂົ້າມາເພື່ອແບ່ງໜ້າຈໍ"</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="maximize_button_text" msgid="1650859196290301963">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ຫຍໍ້ລົງ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ປິດ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ກັບຄືນ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ມືບັງຄັບ"</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="more_button_text" msgid="3655388105592893530">"ເພີ່ມເຕີມ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ລອຍ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
index 91c4a033356d..b84c83555ea0 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ການສະແດງຜົນຊ້ອນກັນ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
- <string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ປິດ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
- <string name="pip_move" msgid="1544227837964635439">"ຍ້າຍ PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"ຂະຫຍາຍ PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"ຫຍໍ້ PIP ລົງ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ກົດ "<annotation icon="home_icon">" HOME "</annotation>" ສອງເທື່ອສຳລັບການຄວບຄຸມ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ຍ້າຍ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ຂະຫຍາຍ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ຫຍໍ້"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ກົດ "<annotation icon="home_icon">"HOME"</annotation>" ສອງເທື່ອສຳລັບການຄວບຄຸມ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ເມນູການສະແດງຜົນຊ້ອນກັນ."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ຍ້າຍໄປຊ້າຍ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ຍ້າຍໄປຂວາ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ຍ້າຍຂຶ້ນ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ຍ້າຍລົງ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ແລ້ວໆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index e2ae643ad308..922f5b59a703 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Nustatymai"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Įjungti išskaidyto ekrano režimą"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Vaizdo vaizde meniu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jei nenorite, kad „<xliff:g id="NAME">%s</xliff:g>“ naudotų šią funkciją, palietę atidarykite nustatymus ir išjunkite ją."</string>
<string name="pip_play" msgid="3496151081459417097">"Leisti"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šią programą galima atidaryti tik viename lange."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skaidyto ekrano daliklis"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Skaidyto ekrano daliklis"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kairysis ekranas viso ekrano režimu"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kairysis ekranas 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kairysis ekranas 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Kai kurios programos geriausiai veikia stačiuoju režimu"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pabandykite naudoti vieną iš šių parinkčių, kad išnaudotumėte visą vietą"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pasukite įrenginį, kad įjungtumėte viso ekrano režimą"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dukart palieskite šalia programos, kad pakeistumėte jos poziciją"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dukart palieskite už programos ribų, kad pakeistumėte jos poziciją"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Išskleiskite, jei reikia daugiau informacijos."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Padidinti"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Sumažinti"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Uždaryti"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atgal"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Rankenėlė"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Stalinio kompiuterio režimas"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Išskaidyto ekrano režimas"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Daugiau"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index 04265ca01b48..0537553cc36a 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Vaizdas vaizde"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Uždaryti"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
- <string name="pip_move" msgid="1544227837964635439">"Perkelti PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Iškleisti PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Sutraukti PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Jei reikia valdiklių, dukart paspauskite "<annotation icon="home_icon">"PAGRINDINIS"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Perkelti"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Išskleisti"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Sutraukti"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Jei reikia valdiklių, dukart pasp. "<annotation icon="home_icon">"PAGRINDINIS"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Vaizdo vaizde meniu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Perkelti kairėn"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Perkelti dešinėn"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Perkelti aukštyn"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Perkelti žemyn"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Atlikta"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index a77160bc262a..08ac9280bbab 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Iestatījumi"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Piekļūt ekrāna sadalīšanas režīmam"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Izvēlne"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Izvēlne attēlam attēlā"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ja nevēlaties lietotnē <xliff:g id="NAME">%s</xliff:g> izmantot šo funkciju, pieskarieties, lai atvērtu iestatījumus un izslēgtu funkciju."</string>
<string name="pip_play" msgid="3496151081459417097">"Atskaņot"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šo lietotni var atvērt tikai vienā logā."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekrāna sadalītājs"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Ekrāna sadalītājs"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kreisā daļa pa visu ekrānu"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pa kreisi 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pa kreisi 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Dažas lietotnes vislabāk darbojas portreta režīmā"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Izmēģiniet vienu no šīm iespējām, lai efektīvi izmantotu pieejamo vietu"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pagrieziet ierīci, lai aktivizētu pilnekrāna režīmu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Veiciet dubultskārienu blakus lietotnei, lai manītu tās pozīciju"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Lai pārvietotu lietotni, veiciet dubultskārienu ārpus lietotnes"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Izvērsiet, lai iegūtu plašāku informāciju."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizēt"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizēt"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Aizvērt"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Atpakaļ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Turis"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna režīms"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Darbvirsmas režīms"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Sadalīt ekrānu"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Vairāk"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
index 8c6191e00833..13baa9bc46eb 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Attēls attēlā"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma bez nosaukuma)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Aizvērt"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
- <string name="pip_move" msgid="1544227837964635439">"Pārvietot attēlu attēlā"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Izvērst “Attēls attēlā” logu"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Sakļaut “Attēls attēlā” logu"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Atvērt vadīklas: divreiz nospiediet pogu "<annotation icon="home_icon">"SĀKUMS"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Pārvietot"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Izvērst"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Sakļaut"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Atvērt vadīklas: divreiz nospiediet pogu "<annotation icon="home_icon">"SĀKUMS"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Izvēlne attēlam attēlā."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Pārvietot pa kreisi"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Pārvietot pa labi"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Pārvietot augšup"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pārvietot lejup"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gatavs"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index bac0c9eee4c2..ae71ae90043f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Поставки"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Влези во поделен екран"</string>
<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_play" msgid="3496151081459417097">"Пушти"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Апликацијава може да се отвори само во еден прозорец."</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="703810061635792791">"Разделник на поделен екран"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Разделник на поделен екран"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Допрете за да ја рестартирате апликацијава за подобар приказ."</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="6688664582871779215">"Некои апликации најдобро работат во режим на портрет"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте една од опцииве за да го извлечете максимумот од вашиот простор"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте го уредот за да отворите на цел екран"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Допрете двапати до некоја апликација за да ја преместите"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Повлечете во друга апликација за поделен екран"</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="maximize_button_text" msgid="1650859196290301963">"Зголеми"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Минимизирај"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затвори"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Прекар"</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="more_button_text" msgid="3655388105592893530">"Повеќе"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index beef1fef862b..d7a9516bea7f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Слика во слика"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Затвори"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
- <string name="pip_move" msgid="1544227837964635439">"Премести PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Прошири ја сликата во слика"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Собери ја сликата во слика"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Притиснете двапати на "<annotation icon="home_icon">" HOME "</annotation>" за контроли"</string>
+ <string name="pip_move" msgid="158770205886688553">"Премести"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Собери"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Притиснете двапати на "<annotation icon="home_icon">"HOME"</annotation>" за контроли"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени за „Слика во слика“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Премести налево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Премести надесно"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Премести нагоре"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Премести надолу"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index de0f837fcd3f..092331284f61 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ക്രമീകരണം"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"സ്ക്രീൻ വിഭജന മോഡിൽ പ്രവേശിക്കുക"</string>
<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_play" msgid="3496151081459417097">"പ്ലേ ചെയ്യുക"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ഈ ആപ്പ് ഒരു വിൻഡോയിൽ മാത്രമേ തുറക്കാനാകൂ."</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="703810061635792791">"സ്പ്ലിറ്റ്-സ്ക്രീൻ ഡിവൈഡർ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"സ്‌ക്രീൻ വിഭജന മോഡ് ഡിവൈഡർ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"മികച്ച കാഴ്‌ചയ്‌ക്കായി ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</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="6688664582871779215">"ചില ആപ്പുകൾ പോർട്രെയ്റ്റിൽ മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്നു"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"നിങ്ങളുടെ ഇടം പരമാവധി പ്രയോജനപ്പെടുത്താൻ ഈ ഓപ്ഷനുകളിലൊന്ന് പരീക്ഷിക്കുക"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ഈ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ഒരു ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ, അതിന് തൊട്ടടുത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"സ്‌ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</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="maximize_button_text" msgid="1650859196290301963">"വലുതാക്കുക"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ചെറുതാക്കുക"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"അടയ്ക്കുക"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"മടങ്ങുക"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ഹാൻഡിൽ"</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="more_button_text" msgid="3655388105592893530">"കൂടുതൽ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ഫ്ലോട്ട്"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index c2a532d09647..56f2b196421b 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ചിത്രത്തിനുള്ളിൽ ചിത്രം"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
+ <string name="pip_close" msgid="2955969519031223530">"അടയ്ക്കുക"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP നീക്കുക"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP വികസിപ്പിക്കുക"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP ചുരുക്കുക"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" നിയന്ത്രണങ്ങൾക്കായി "<annotation icon="home_icon">" ഹോം "</annotation>" രണ്ട് തവണ അമർത്തുക"</string>
+ <string name="pip_move" msgid="158770205886688553">"നീക്കുക"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"വികസിപ്പിക്കുക"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ചുരുക്കുക"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"നിയന്ത്രണങ്ങൾക്കായി "<annotation icon="home_icon">"ഹോം "</annotation>" രണ്ട് തവണ അമർത്തുക"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ചിത്രത്തിനുള്ളിൽ ചിത്രം മെനു."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"വലത്തേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"മുകളിലേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"താഴേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"പൂർത്തിയായി"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 1205306e0833..c1950a1d3241 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Хуваасан дэлгэцийг оруулна уу"</string>
<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_play" msgid="3496151081459417097">"Тоглуулах"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</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="703810061635792791">"\"Дэлгэц хуваах\" хуваагч"</string>
+ <string name="divider_title" msgid="5482989479865361192">"\"Дэлгэцийг хуваах\" хуваагч"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү."</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="6688664582871779215">"Зарим апп нь босоо чиглэлд хамгийн сайн ажилладаг"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Орон зайгаа сайтар ашиглахын тулд эдгээр сонголтуудын аль нэгийг туршиж үзээрэй"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Төхөөрөмжөө бүтэн дэлгэцээр үзэхийн тулд эргүүлнэ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Аппыг дахин байрлуулахын тулд хажууд нь хоёр товшино"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Дэлгэцийг хуваахын тулд өөр апп руу чирнэ үү"</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="maximize_button_text" msgid="1650859196290301963">"Томруулах"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Багасгах"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Хаах"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Буцах"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Бариул"</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="more_button_text" msgid="3655388105592893530">"Бусад"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Хөвөгч"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index bf8c59b57359..0e6dcca17e38 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Дэлгэц доторх дэлгэц"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Хаах"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP-г зөөх"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP-г дэлгэх"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP-г хураах"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Хяналтад хандах бол "<annotation icon="home_icon">" HOME "</annotation>" дээр хоёр дарна уу"</string>
+ <string name="pip_move" msgid="158770205886688553">"Зөөх"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Дэлгэх"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Хураах"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Хяналтад хандах бол "<annotation icon="home_icon">"HOME"</annotation>" дээр хоёр дарна уу"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Дэлгэцэн доторх дэлгэцийн цэс."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Зүүн тийш зөөх"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Баруун тийш зөөх"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Дээш зөөх"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Доош зөөх"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Болсон"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index c91d06fdf3d5..29821f6a8bbc 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग्ज"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रीन एंटर करा"</string>
<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_play" msgid="3496151081459417097">"प्ले करा"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"हे अ‍ॅप फक्त एका विंडोमध्ये उघडले जाऊ शकते."</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="703810061635792791">"विभाजित-स्क्रीन विभाजक"</string>
+ <string name="divider_title" msgid="5482989479865361192">"स्प्लिट-स्क्रीन विभाजक"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"अधिक चांगल्या व्ह्यूसाठी हे अ‍ॅप रीस्टार्ट करण्याकरिता टॅप करा."</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="6688664582871779215">"काही ॲप्स पोर्ट्रेटमध्ये सर्वोत्तम काम करतात"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तुमच्या स्पेसचा पुरेपूर वापर करण्यासाठी, यांपैकी एक पर्याय वापरून पहा"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फुल स्क्रीन करण्यासाठी, तुमचे डिव्हाइस फिरवा"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या शेजारी दोनदा टॅप करा"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट-स्क्रीन वापरण्यासाठी दुसऱ्या ॲपमध्ये ड्रॅग करा"</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="maximize_button_text" msgid="1650859196290301963">"मोठे करा"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"लहान करा"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बंद करा"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"मागे जा"</string>
+ <string name="handle_text" msgid="1766582106752184456">"हँडल"</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="more_button_text" msgid="3655388105592893530">"आणखी"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index 5d519b7afe9a..8a8977979217 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"चित्रात-चित्र"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
+ <string name="pip_close" msgid="2955969519031223530">"बंद करा"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP हलवा"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP चा विस्तार करा"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP कोलॅप्स करा"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" नियंत्रणांसाठी "<annotation icon="home_icon">" होम "</annotation>" दोनदा दाबा"</string>
+ <string name="pip_move" msgid="158770205886688553">"हलवा"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"विस्तार करा"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"कोलॅप्स करा"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"नियंत्रणांसाठी "<annotation icon="home_icon">"होम"</annotation>" दोनदा दाबा"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"चित्रात-चित्र मेनू."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"डावीकडे हलवा"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"उजवीकडे हलवा"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"वर हलवा"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"खाली हलवा"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"पूर्ण झाले"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 652a9919d163..c3db19d46a55 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk skrin pisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Gambar dalam Gambar"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika anda tidak mahu <xliff:g id="NAME">%s</xliff:g> menggunakan ciri ini, ketik untuk membuka tetapan dan matikan ciri."</string>
<string name="pip_play" msgid="3496151081459417097">"Main"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Apl ini hanya boleh dibuka dalam 1 tetingkap."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Pembahagi skrin pisah"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Pembahagi skrin pisah"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrin penuh kiri"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sesetengah apl berfungsi paling baik dalam mod potret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Cuba salah satu daripada pilihan ini untuk memanfaatkan ruang anda sepenuhnya"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar peranti anda untuk beralih ke skrin penuh"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketik dua kali bersebelahan apl untuk menempatkan semula apl"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Seret apl lain untuk skrin pisah"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketik dua kali di luar apl untuk menempatkan semula apl itu"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kembangkan untuk mendapatkan maklumat lanjut."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimumkan"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimumkan"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Pemegang"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Mod Desktop"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Lagi"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Terapung"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index 08642c47c91a..afea48d7b510 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Gambar dalam Gambar"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Tutup"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
- <string name="pip_move" msgid="1544227837964635439">"Alihkan PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Kembangkan PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Kuncupkan PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tekan dua kali "<annotation icon="home_icon">" LAMAN UTAMA "</annotation>" untuk mengakses kawalan"</string>
+ <string name="pip_move" msgid="158770205886688553">"Alih"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Kembangkan"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Kuncupkan"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Tekan dua kali "<annotation icon="home_icon">"LAMAN UTAMA"</annotation>" untuk mengakses kawalan"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Gambar dalam Gambar."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Alih ke kiri"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Alih ke kanan"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Alih ke atas"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Alih ke bawah"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Selesai"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 15d182c6451e..b2bb37dd7730 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသို့ ဝင်ရန်"</string>
<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_play" msgid="3496151081459417097">"ဖွင့်ရန်"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ။"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ဤအက်ပ်ကို ဝင်းဒိုး ၁ ခုတွင်သာ ဖွင့်နိုင်သည်။"</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="703810061635792791">"မျက်နှာပြင်ခွဲခြမ်း ပိုင်းခြားပေးသည့်စနစ်"</string>
+ <string name="divider_title" msgid="5482989479865361192">"မျက်နှာပြင်ခွဲ၍ပြသသည့် စနစ်"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ဘယ်ဘက် မျက်နှာပြင်အပြည့်"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ဘယ်ဘက်မျက်နှာပြင် ၇၀%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ဘယ်ဘက် မျက်နှာပြင် ၅၀%"</string>
@@ -66,19 +69,29 @@
<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_got_it" msgid="3382046149225428296">"ရပြီ"</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="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="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့နိုင်သည်။"</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="6688664582871779215">"အချို့အက်ပ်များသည် ဒေါင်လိုက်တွင် အကောင်းဆုံးလုပ်ဆောင်သည်"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"သင့်နေရာကို အကောင်းဆုံးအသုံးပြုနိုင်ရန် ဤရွေးစရာများထဲမှ တစ်ခုကို စမ်းကြည့်ပါ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ဖန်သားပြင်အပြည့်လုပ်ရန် သင့်စက်ကို လှည့်နိုင်သည်"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"အက်ပ်နေရာပြန်ချရန် ၎င်းဘေးတွင် နှစ်ချက်တို့နိုင်သည်"</string>
- <string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"မျက်နှာပြင် ခွဲ၍ပြသနိုင်ရန် နောက်အက်ပ်တစ်ခုကို ဖိဆွဲပါ"</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="maximize_button_text" msgid="1650859196290301963">"ချဲ့ရန်"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ချုံ့ရန်"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ပိတ်ရန်"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"နောက်သို့"</string>
+ <string name="handle_text" msgid="1766582106752184456">"သုံးသူအမည်"</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="more_button_text" msgid="3655388105592893530">"ပိုပြပါ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"မျှောရန်"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
index e01daee115ca..f3ed65da43da 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"နှစ်ခုထပ်၍ကြည့်ခြင်း"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ပိတ်ရန်"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP ရွှေ့ရန်"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP ကို ချဲ့ရန်"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP ကို လျှော့ပြပါ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ထိန်းချုပ်မှုအတွက် "<annotation icon="home_icon">" ပင်မခလုတ် "</annotation>" နှစ်ချက်နှိပ်ပါ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ရွှေ့ရန်"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ချဲ့ရန်"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"လျှော့ပြရန်"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ထိန်းချုပ်မှုအတွက် "<annotation icon="home_icon">" ပင်မခလုတ် "</annotation>" ကို နှစ်ချက်နှိပ်ပါ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"နှစ်ခုထပ်၍ ကြည့်ခြင်းမီနူး။"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ဘယ်သို့ရွှေ့ရန်"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ညာသို့ရွှေ့ရန်"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"အပေါ်သို့ရွှေ့ရန်"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"အောက်သို့ရွှေ့ရန်"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ပြီးပြီ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 9fd42b2f129c..90b9dfca6116 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Innstillinger"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Aktivér delt skjerm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Bilde-i-bilde-meny"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Hvis du ikke vil at <xliff:g id="NAME">%s</xliff:g> skal bruke denne funksjonen, kan du trykke for å åpne innstillingene og slå den av."</string>
<string name="pip_play" msgid="3496151081459417097">"Spill av"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne appen kan bare åpnes i ett vindu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skilleelement for delt skjerm"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Skilleelement for delt skjerm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Utvid den venstre delen av skjermen til hele skjermen"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sett størrelsen på den venstre delen av skjermen til 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sett størrelsen på den venstre delen av skjermen til 50 %"</string>
@@ -63,8 +66,8 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat med bobler"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne bobler. Dra for å flytte dem."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrollér bobler når som helst"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne en boble. Dra for å flytte den."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontroller bobler når som helst"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Trykk på Administrer for å slå av bobler for denne appen"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Trykk for å starte denne appen på nytt for bedre visning."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Noen apper fungerer best i stående format"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv et av disse alternativene for å få mest mulig ut av plassen din"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Roter enheten for å starte fullskjerm"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dobbelttrykk ved siden av en app for å flytte den"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dra inn en annen app for å bruke delt skjerm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dobbelttrykk utenfor en app for å flytte den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vis for å få mer informasjon."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimer"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Lukk"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Tilbake"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Håndtak"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Skrivebordmodus"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Delt skjerm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
index 65ed0b7f5bff..1402e3c9c9bc 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bilde-i-bilde"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Lukk"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
- <string name="pip_move" msgid="1544227837964635439">"Flytt BIB"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Vis BIB"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Skjul BIB"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Dobbelttrykk på "<annotation icon="home_icon">"HJEM"</annotation>" for å åpne kontroller"</string>
+ <string name="pip_move" msgid="158770205886688553">"Flytt"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Vis"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Skjul"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Dobbelttrykk på "<annotation icon="home_icon">"HJEM"</annotation>" for å åpne kontrollene"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Bilde-i-bilde-meny."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flytt til venstre"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flytt til høyre"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Flytt opp"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Flytt ned"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Ferdig"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 8dfec88cc29d..15f22f7ff2d3 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिङहरू"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रिन मोड प्रयोग गर्नुहोस्"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"मेनु"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"\"picture-in-picture\" मेनु"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले सुविधा प्रयोग नगरोस् भन्ने चाहनुहुन्छ भने ट्याप गरेर सेटिङहरू खोल्नुहोस् र यसलाई निष्क्रिय पार्नुहोस्।"</string>
<string name="pip_play" msgid="3496151081459417097">"प्ले गर्नुहोस्"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"यो एप एउटा विन्डोमा मात्र खोल्न मिल्छ।"</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="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
+ <string name="divider_title" msgid="5482989479865361192">"स्प्लिट स्क्रिन डिभाइडर"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"बायाँ भाग ७०%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बायाँ भाग ५०%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"यो एप अझ राम्रो हेर्न मिल्ने बनाउनका लागि यसलाई रिस्टार्ट गर्न ट्याप गर्नुहोस्।"</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="6688664582871779215">"केही एपहरूले पोर्ट्रेटमा राम्रोसँग काम गर्छन्"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तपाईं स्क्रिनको अधिकतम ठाउँ प्रयोग गर्न चाहनुहुन्छ भने यीमध्ये कुनै विकल्प प्रयोग गरी हेर्नुहोस्"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"तपाईं फुल स्क्रिन मोड हेर्न चाहनुहुन्छ भने आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको छेउमा डबल ट्याप गर्नुहोस्"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</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="maximize_button_text" msgid="1650859196290301963">"ठुलो बनाउनुहोस्"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"मिनिमाइज गर्नुहोस्"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बन्द गर्नुहोस्"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"पछाडि"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ह्यान्डल"</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="more_button_text" msgid="3655388105592893530">"थप"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index d33fed67efb6..2b1f20fba4e2 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
+ <string name="pip_close" msgid="2955969519031223530">"बन्द गर्नुहोस्"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP सार्नुहोस्"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP विन्डो एक्स्पान्ड गर्नु…"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP विन्डो कोल्याप्स गर्नुहोस्"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" कन्ट्रोल मेनु खोल्न "<annotation icon="home_icon">" होम "</annotation>" बटन दुई पटक थिच्नुहोस्"</string>
+ <string name="pip_move" msgid="158770205886688553">"सार्नुहोस्"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"एक्स्पान्ड गर्नुहोस्"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"कोल्याप्स गर्नुहोस्"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"कन्ट्रोल मेनु खोल्न "<annotation icon="home_icon">" होम "</annotation>" बटन दुई पटक थिच्नुहोस्"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"picture-in-picture\" मेनु।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"बायाँतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"दायाँतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"माथितिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"तलतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"सम्पन्न भयो"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 8468b04c66da..f9f4ef4a4b63 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Gesplitst scherm openen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Scherm-in-scherm-menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en zet je de functie uit."</string>
<string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Deze app kan slechts in 1 venster worden geopend."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Scheiding voor gesplitst scherm"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Scheiding voor gesplitst scherm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Linkerscherm op volledig scherm"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Linkerscherm 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Linkerscherm 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tik om deze app opnieuw op te starten voor een betere weergave."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige apps werken het best in de staande stand"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van deze opties om optimaal gebruik te maken van je ruimte"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai je apparaat om naar volledig scherm te schakelen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Uitvouwen voor meer informatie."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximaliseren"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimaliseren"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sluiten"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Gebruikersnaam"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Gesplitst scherm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Zwevend"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index 9763c5665ab2..6766773fa866 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Scherm-in-scherm"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Sluiten"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
- <string name="pip_move" msgid="1544227837964635439">"SIS verplaatsen"</string>
- <string name="pip_expand" msgid="7605396312689038178">"SIS uitvouwen"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"SIS samenvouwen"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Druk twee keer op "<annotation icon="home_icon">" HOME "</annotation>" voor bedieningselementen"</string>
+ <string name="pip_move" msgid="158770205886688553">"Verplaatsen"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Uitvouwen"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Samenvouwen"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Druk 2 keer op "<annotation icon="home_icon">"HOME"</annotation>" voor bedieningsopties"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Scherm-in-scherm-menu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Naar links verplaatsen"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Naar rechts verplaatsen"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Omhoog verplaatsen"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Omlaag verplaatsen"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klaar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index a8d8448edf99..5a76a6f3f22a 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ସେଟିଂସ୍"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ମୋଡ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<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_play" msgid="3496151081459417097">"ପ୍ଲେ କରନ୍ତୁ"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</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="703810061635792791">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"ସ୍ପ୍ଲିଟ-ସ୍କ୍ରିନ ଡିଭାଇଡର"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</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="6688664582871779215">"କିଛି ଆପ ପୋର୍ଟ୍ରେଟରେ ସବୁଠାରୁ ଭଲ କାମ କରେ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ଆପଣଙ୍କ ସ୍ପେସରୁ ଅଧିକ ଲାଭ ପାଇବାକୁ ଏହି ବିକଳ୍ପଗୁଡ଼ିକ ମଧ୍ୟରୁ ଗୋଟିଏ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ପୂର୍ଣ୍ଣ-ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହା ପାଖରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ସ୍ପ୍ଲିଟ-ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</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="maximize_button_text" msgid="1650859196290301963">"ବଡ଼ କରନ୍ତୁ"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ଛୋଟ କରନ୍ତୁ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ହେଣ୍ଡେଲ"</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="more_button_text" msgid="3655388105592893530">"ଅଧିକ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ଫ୍ଲୋଟ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
index e0344855bd1f..1e81f4d80f32 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ପିକଚର୍-ଇନ୍-ପିକଚର୍"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIPକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIPକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIPକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ପାଇଁ "<annotation icon="home_icon">" ହୋମ ବଟନ "</annotation>"କୁ ଦୁଇଥର ଦବାନ୍ତୁ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ମୁଭ କରନ୍ତୁ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ବିସ୍ତାର କରନ୍ତୁ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ପାଇଁ "<annotation icon="home_icon">"ହୋମ ବଟନ"</annotation>"କୁ ଦୁଇଥର ଦବାନ୍ତୁ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ପିକଚର-ଇନ-ପିକଚର ମେନୁ।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ଉପରକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ତଳକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ହୋଇଗଲା"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index f99176cb682d..617c95eec8d9 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ਸੈਟਿੰਗਾਂ"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
<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_play" msgid="3496151081459417097">"ਚਲਾਓ"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</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="703810061635792791">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਡਿਵਾਈਡਰ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਵਿਭਾਜਕ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਲਈ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</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="6688664582871779215">"ਕੁਝ ਐਪਾਂ ਪੋਰਟਰੇਟ ਵਿੱਚ ਬਿਹਤਰ ਕੰਮ ਕਰਦੀਆਂ ਹਨ"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ਆਪਣੀ ਜਗ੍ਹਾ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਲਾਹਾ ਲੈਣ ਲਈ ਇਨ੍ਹਾਂ ਵਿਕਲਪਾਂ ਵਿੱਚੋਂ ਕੋਈ ਇੱਕ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁਮਾਓ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਅੱਗੇ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</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="maximize_button_text" msgid="1650859196290301963">"ਵੱਡਾ ਕਰੋ"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ਛੋਟਾ ਕਰੋ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ਬੰਦ ਕਰੋ"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ਪਿੱਛੇ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ਹੈਂਡਲ"</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="more_button_text" msgid="3655388105592893530">"ਹੋਰ"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ਫ਼ਲੋਟ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index 9c01ac3f3cc0..758aafad457a 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ਬੰਦ ਕਰੋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP ਨੂੰ ਲਿਜਾਓ"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP ਨੂੰ ਸਮੇਟੋ"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" ਕੰਟਰੋਲਾਂ ਲਈ "<annotation icon="home_icon">" ਹੋਮ ਬਟਨ "</annotation>" ਨੂੰ ਦੋ ਵਾਰ ਦਬਾਓ"</string>
+ <string name="pip_move" msgid="158770205886688553">"ਲਿਜਾਓ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ਸਮੇਟੋ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"ਕੰਟਰੋਲਾਂ ਲਈ "<annotation icon="home_icon">"ਹੋਮ"</annotation>" ਨੂੰ ਦੋ ਵਾਰ ਦਬਾਓ"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਮੀਨੂ।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ਉੱਪਰ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ਹੋ ਗਿਆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index f2147c04d335..4a17ec74bada 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ustawienia"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Włącz podzielony ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu funkcji Obraz w obrazie."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jeśli nie chcesz, by aplikacja <xliff:g id="NAME">%s</xliff:g> korzystała z tej funkcji, otwórz ustawienia i wyłącz ją."</string>
<string name="pip_play" msgid="3496151081459417097">"Odtwórz"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ta aplikacja może być otwarta tylko w 1 oknie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Linia dzielenia ekranu"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Linia dzielenia ekranu"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lewa część ekranu na pełnym ekranie"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% lewej części ekranu"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% lewej części ekranu"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Kliknij, aby zrestartować aplikację i zyskać lepszą widoczność."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektóre aplikacje działają najlepiej w orientacji pionowej"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Wypróbuj jedną z tych opcji, aby jak najlepiej wykorzystać miejsce"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Obróć urządzenie, aby przejść do pełnego ekranu"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kliknij dwukrotnie obok aplikacji, aby ją przenieść"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Przeciągnij drugą aplikację, aby podzielić ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kliknij dwukrotnie poza aplikacją, aby ją przenieść"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozwiń, aby wyświetlić więcej informacji."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksymalizuj"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimalizuj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zamknij"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Wstecz"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Uchwyt"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Pełny ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Tryb pulpitu"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Podzielony ekran"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Więcej"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Pływające"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
index b922e2d5a6ba..b598351b5127 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Obraz w obrazie"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez tytułu)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zamknij"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"Przenieś PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Rozwiń PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Zwiń PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Naciśnij dwukrotnie "<annotation icon="home_icon">"EKRAN GŁÓWNY"</annotation>", aby wyświetlić ustawienia"</string>
+ <string name="pip_move" msgid="158770205886688553">"Przenieś"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Rozwiń"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Zwiń"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Naciśnij dwukrotnie "<annotation icon="home_icon">"EKRAN GŁÓWNY"</annotation>", aby wyświetlić ustawienia"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu funkcji Obraz w obrazie."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Przenieś w lewo"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Przenieś w prawo"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Przenieś w górę"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Przenieś w dół"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gotowe"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 2efc5543dd87..69be68ead461 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Dividir tela"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu do picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Este app só pode ser aberto em uma única janela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor de tela"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualizaçã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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outro app para a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index cc4eb3c32c1f..2528ea9b8ebb 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Abrir picture-in-picture"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Fechar picture-in-picture"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Pressione o botão "<annotation icon="home_icon">"HOME"</annotation>" duas vezes para acessar os controles"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index c68a6934dead..13e83ac0bcf7 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Definições"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Aceder ao ecrã dividido"</string>
<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_play" msgid="3496151081459417097">"Reproduzir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app só pode ser aberta em 1 janela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A app não é compatível com o início em ecrãs secundários."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor do ecrã dividido"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor do ecrã dividido"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ecrã esquerdo inteiro"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% no ecrã esquerdo"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% no ecrã esquerdo"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ficar com uma melhor visão."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algumas apps funcionam melhor no modo vertical"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Experimente uma destas opções para aproveitar ao máximo o seu espaço"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rode o dispositivo para ficar em ecrã inteiro"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes junto a uma app para a reposicionar"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outra app para usar o ecrã dividido"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de uma app para a reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expandir para obter mais informações"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Anterior"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Indicador"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo de ambiente de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index c4ae78d89ba8..a678f581c272 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Ecrã no ecrã"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover Ecrã no ecrã"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Expandir Ecrã no ecrã"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Reduzir Ecrã no ecrã"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Prima duas vezes "<annotation icon="home_icon">" PÁGINA INICIAL "</annotation>" para controlos"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Reduzir"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Prima duas vezes "<annotation icon="home_icon">"PÁGINA INICIAL"</annotation>" para os controlos"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu de ecrã no ecrã."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 2efc5543dd87..69be68ead461 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Dividir tela"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu do picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Se você não quer que o app <xliff:g id="NAME">%s</xliff:g> use este recurso, toque para abrir as configurações e desativá-lo."</string>
<string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Este app só pode ser aberto em uma única janela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divisor de tela"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualizaçã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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outro app para a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index cc4eb3c32c1f..2528ea9b8ebb 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Abrir picture-in-picture"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Fechar picture-in-picture"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Pressione o botão "<annotation icon="home_icon">"HOME"</annotation>" duas vezes para acessar os controles"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 804d34f980ff..c112a9d26a7c 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -17,25 +17,28 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="pip_phone_close" msgid="5783752637260411309">"Închideți"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string>
+ <string name="pip_phone_close" msgid="5783752637260411309">"Închide"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Extinde"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesați ecranul împărțit"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesează ecranul împărțit"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meniu picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
- <string name="pip_play" msgid="3496151081459417097">"Redați"</string>
- <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string>
- <string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
- <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
- <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
- <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stocați"</string>
- <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulați stocarea"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu vrei ca <xliff:g id="NAME">%s</xliff:g> să folosească această funcție, atinge pentru a deschide setările și dezactiveaz-o."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Redă"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Întrerupe"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Treci la următorul"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treci la cel anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionează"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stochează"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulează stocarea"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplicația poate fi deschisă într-o singură fereastră."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Separator pentru ecranul împărțit"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Separator pentru ecranul împărțit"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Partea stângă pe ecran complet"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Partea stângă: 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Partea stângă: 50%"</string>
@@ -47,38 +50,48 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Partea de sus: 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activați modul cu o mână"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Părăsiți modul cu o mână"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisează în sus din partea de jos a ecranului sau atinge oriunde deasupra ferestrei aplicației"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activează modul cu o mână"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ieși din modul cu o mână"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string>
- <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adăugați înapoi în stivă"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adaugă înapoi în stivă"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <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> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mută în stânga sus"</string>
+ <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="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ți balonul"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</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>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atingeți pentru a deschide balonul. Trageți pentru a-l muta."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlați oricând baloanele"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Atingeți Gestionați pentru a dezactiva baloanele din această aplicație"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atinge pentru a deschide balonul. Trage pentru a-l muta."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlează oricând baloanele"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Atinge Gestionează pentru a dezactiva baloanele din această aplicație"</string>
<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="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</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="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
- <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
- <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
- <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Unele aplicații funcționează cel mai bine în orientarea portret"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Încercați una dintre aceste opțiuni pentru a profita din plin de spațiu"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotiți dispozitivul pentru a trece în modul ecran complet"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Atingeți de două ori lângă o aplicație pentru a o repoziționa"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Atinge ca să repornești aplicația pentru o vizualizare mai bună."</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>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extinde pentru mai multe informații"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizează"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Înapoi"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Ghidaj"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modul desktop"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ecran împărțit"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mai multe"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index 86a30f49df15..c3226ff28934 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Închideți PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Închide"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
- <string name="pip_move" msgid="1544227837964635439">"Mutați fereastra PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Extindeți fereastra PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Restrângeți fereastra PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Apăsați de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mută"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Extinde"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Restrânge"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Apasă de 2 ori "<annotation icon="home_icon">"ECRANUL DE PORNIRE"</annotation>" pentru comenzi"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meniu picture-in-picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mută la stânga"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mută la dreapta"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mută în sus"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mută în jos"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gata"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 95bf1cf11435..489adc06e93d 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Включить разделение экрана"</string>
<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_play" msgid="3496151081459417097">"Воспроизвести"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Это приложение можно открыть только в одном окне."</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="703810061635792791">"Разделитель экрана"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Разделитель экрана"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Нажмите, чтобы перезапустить приложение и настроить удобный для просмотра вид"</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="6688664582871779215">"Некоторые приложения лучше работают в вертикальном режиме"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Чтобы эффективно использовать экранное пространство, выполните одно из следующих действий:"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Чтобы перейти в полноэкранный режим, поверните устройство."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Чтобы переместить приложение, нажмите на него дважды."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</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="maximize_button_text" msgid="1650859196290301963">"Развернуть"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Свернуть"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрыть"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Маркер"</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="more_button_text" msgid="3655388105592893530">"Ещё"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Плавающее окно"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index 08623e1e69c5..c8fb47913ec9 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Картинка в картинке"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
- <string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Закрыть"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
- <string name="pip_move" msgid="1544227837964635439">"Переместить PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Развернуть PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Свернуть PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Элементы управления: дважды нажмите "<annotation icon="home_icon">" кнопку главного экрана "</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Переместить"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Развернуть"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Свернуть"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Параметры: дважды нажмите "<annotation icon="home_icon">"кнопку главного экрана"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню \"Картинка в картинке\"."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Переместить влево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Переместить вправо"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Переместить вверх"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Переместить вниз"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 23dd65ad7b31..32371148fce2 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"සැකසීම්"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"බෙදුම් තිරයට ඇතුළු වන්න"</string>
<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_play" msgid="3496151081459417097">"ධාවනය කරන්න"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්‍රියා නොකළ හැකිය"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</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="703810061635792791">"බෙදුම්-තිර වෙන්කරණය"</string>
+ <string name="divider_title" msgid="5482989479865361192">"බෙදුම්-තිර වෙන්කරණය"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"වඩා හොඳ දසුනක් ලබා ගැනීම සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න."</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="6688664582871779215">"සමහර යෙදුම් ප්‍රතිමූර්තිය තුළ හොඳින්ම ක්‍රියා කරයි"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ඔබගේ ඉඩෙන් උපරිම ප්‍රයෝජන ගැනීමට මෙම විකල්පවලින් එකක් උත්සාහ කරන්න"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"සම්පූර්ණ තිරයට යාමට ඔබගේ උපාංගය කරකවන්න"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"එය නැවත ස්ථානගත කිරීමට යෙදුමකට යාබදව දෙවරක් තට්ටු කරන්න"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</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="maximize_button_text" msgid="1650859196290301963">"විහිදන්න"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"කුඩා කරන්න"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"වසන්න"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"ආපසු"</string>
+ <string name="handle_text" msgid="1766582106752184456">"හැඬලය"</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="more_button_text" msgid="3655388105592893530">"තව"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"පාවෙන"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index fbb0ebba0623..aa949ec75b3d 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"පින්තූරය-තුළ-පින්තූරය"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
+ <string name="pip_close" msgid="2955969519031223530">"වසන්න"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP ගෙන යන්න"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP දිග හරින්න"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP හකුළන්න"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" පාලන සඳහා "<annotation icon="home_icon">" මුල් පිටුව "</annotation>" දෙවරක් ඔබන්න"</string>
+ <string name="pip_move" msgid="158770205886688553">"ගෙන යන්න"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"දිග හරින්න"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"හකුළන්න"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"පාලන සඳහා "<annotation icon="home_icon">"නිවහන"</annotation>" දෙවරක් ඔබන්න"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"පින්තූරය තුළ පින්තූරය මෙනුව"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"වමට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"දකුණට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ඉහළට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"පහළට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"නිමයි"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index a231cacefb20..a7530215d010 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavenia"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Prejsť na rozdelenú obrazovku"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Ponuka"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Ponuka obrazu v obraze"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ak nechcete, aby aplikácia <xliff:g id="NAME">%s</xliff:g> používala túto funkciu, klepnutím otvorte nastavenia a vypnite ju."</string>
<string name="pip_play" msgid="3496151081459417097">"Prehrať"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Táto aplikácia môže byť otvorená iba v jednom okne."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Rozdeľovač obrazovky"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Rozdeľovač obrazovky"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ľavá – na celú obrazovku"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ľavá – 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ľavá – 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektoré aplikácie fungujú najlepšie v režime na výšku"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Vyskúšajte jednu z týchto možností a využívajte svoj priestor naplno"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zariadenia prejdete do režimu celej obrazovky"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedľa aplikácie zmeníte jej pozíciu"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Rozdelenú obrazovku aktivujete presunutím ďalšie aplikácie"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikácie zmeníte jej pozíciu"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Po rozbalení sa dozviete viac."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovať"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimalizovať"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zavrieť"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Späť"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Rukoväť"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Režim počítača"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Viac"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index 81cb0eafc759..d5562d519d4b 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Obraz v obraze"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zavrieť"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
- <string name="pip_move" msgid="1544227837964635439">"Presunúť obraz v obraze"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Rozbaliť obraz v obraze"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Zbaliť obraz v obraze"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Ovládanie zobraz. dvoj. stlač. "<annotation icon="home_icon">" TLAČIDLA PLOCHY "</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Presunúť"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Rozbaliť"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Zbaliť"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Ovládanie zobrazíte dvojitým stlačením "<annotation icon="home_icon">"DOMOV"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Ponuka obrazu v obraze."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Posunúť doľava"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Posunúť doprava"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Posunúť nahor"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Posunúť nadol"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index adeaae978eaa..b5d87333d85e 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavitve"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Vklopi razdeljen zaslon"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni za sliko v sliki"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v načinu slika v sliki"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Če ne želite, da aplikacija <xliff:g id="NAME">%s</xliff:g> uporablja to funkcijo, se dotaknite, da odprete nastavitve, in funkcijo izklopite."</string>
<string name="pip_play" msgid="3496151081459417097">"Predvajaj"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"To aplikacijo je mogoče odpreti samo v enem oknu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdelilnik zaslonov"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Razdelilnik zaslonov"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levi v celozaslonski način"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nekatere aplikacije najbolje delujejo v navpični postavitvi"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Poskusite eno od teh možnosti za čim boljši izkoristek prostora"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Če želite preklopiti v celozaslonski način, zasukajte napravo."</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvakrat se dotaknite ob aplikaciji, če jo želite prestaviti."</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvakrat se dotaknite zunaj aplikacije, če jo želite prestaviti."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Razširitev za več informacij"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiraj"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimiraj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zapri"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Nazaj"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Ročica"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Namizni način"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Razdeljen zaslon"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Več"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Lebdeče"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
index 060aaa0ce647..a37375e1ae9c 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Slika v sliki"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program brez naslova)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Zapri"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
- <string name="pip_move" msgid="1544227837964635439">"Premakni sliko v sliki"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Razširi sliko v sliki"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Strni sliko v sliki"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Za kontrolnike dvakrat pritisnite gumb za "<annotation icon="home_icon">" ZAČETNI ZASLON "</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Premakni"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Razširi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Strni"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Za kontrolnike dvakrat pritisnite gumb za "<annotation icon="home_icon">"ZAČETNI ZASLON"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meni za sliko v sliki"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Premakni levo"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Premakni desno"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Premakni navzgor"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Premakni navzdol"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Končano"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 2839b4bae7e4..ebd644c7425f 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Hyr në ekranin e ndarë"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyja"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menyja e \"Figurës brenda figurës\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Nëse nuk dëshiron që <xliff:g id="NAME">%s</xliff:g> ta përdorë këtë funksion, trokit për të hapur cilësimet dhe për ta çaktivizuar."</string>
<string name="pip_play" msgid="3496151081459417097">"Luaj"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ky aplikacion mund të hapet vetëm në 1 dritare."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ndarësi i ekranit të ndarë"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Ndarësi i ekranit të ndarë"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ekrani i plotë majtas"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Majtas 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Majtas 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Disa aplikacione funksionojnë më mirë në modalitetin vertikal"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Provo një nga këto opsione për ta shfrytëzuar sa më mirë hapësirën"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rrotullo ekranin për të kaluar në ekran të plotë"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Trokit dy herë pranë një aplikacioni për ta ripozicionuar"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Trokit dy herë jashtë një aplikacioni për ta ripozicionuar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje për më shumë informacion."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizo"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimizo"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Mbyll"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Pas"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Emërtimi"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Modaliteti i desktopit"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ekrani i ndarë"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Më shumë"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
index 9bfdb6a3edd8..3fbaaac2d3a2 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Figurë brenda figurës"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Mbyll"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
- <string name="pip_move" msgid="1544227837964635439">"Zhvendos PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Zgjero PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Palos PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Trokit dy herë "<annotation icon="home_icon">" KREU "</annotation>" për kontrollet"</string>
+ <string name="pip_move" msgid="158770205886688553">"Lëviz"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Zgjero"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Palos"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Trokit dy herë te "<annotation icon="home_icon">"KREU"</annotation>" për kontrollet"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menyja e \"Figurës brenda figurës\"."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Lëviz majtas"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Lëviz djathtas"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Lëviz lart"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Lëviz poshtë"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"U krye"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 9db6b7c63610..d051ca350682 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Подешавања"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Уђи на подељени екран"</string>
<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_play" msgid="3496151081459417097">"Пусти"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ова апликација може да се отвори само у једном прозору."</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="703810061635792791">"Разделник подељеног екрана"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Разделник подељеног екрана"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Додирните да бисте рестартовали ову апликацију ради бољег приказа."</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="6688664582871779215">"Неке апликације најбоље функционишу у усправном режиму"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте једну од ових опција да бисте на најбољи начин искористили простор"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте уређај за приказ преко целог екрана"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двапут додирните поред апликације да бисте променили њену позицију"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Превуците другу апликацију да бисте користили подељени екран"</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="maximize_button_text" msgid="1650859196290301963">"Увећајте"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Умањите"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затворите"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Идентификатор"</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="more_button_text" msgid="3655388105592893530">"Још"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Плутајуће"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
index 6bc5c87bab48..34950027772b 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Слика у слици"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Затвори"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
- <string name="pip_move" msgid="1544227837964635439">"Премести слику у слици"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Прошири слику у слици"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Скупи слику у слици"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Двапут притисните "<annotation icon="home_icon">" HOME "</annotation>" за контроле"</string>
+ <string name="pip_move" msgid="158770205886688553">"Премести"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Скупи"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Двапут притисните "<annotation icon="home_icon">" HOME "</annotation>" за контроле"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени Слика у слици."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Померите налево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Померите надесно"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Померите нагоре"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Померите надоле"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index f6bd55423cdc..cd46039b26b5 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Inställningar"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Starta delad skärm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Bild-i-bild-meny"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Om du inte vill att den här funktionen används i <xliff:g id="NAME">%s</xliff:g> öppnar du inställningarna genom att trycka. Sedan inaktiverar du funktionen."</string>
<string name="pip_play" msgid="3496151081459417097">"Spela upp"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denna app kan bara vara öppen i ett fönster."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan inte köras på en sekundär skärm."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Avdelare för delad skärm"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Avdelare för delad skärm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Helskärm på vänster skärm"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vänster 70 %"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vänster 50 %"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tryck för att starta om appen och få en bättre vy."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Vissa appar fungerar bäst i stående läge"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Testa med ett av dessa alternativ för att få ut mest möjliga av ytan"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotera skärmen för att gå över till helskärmsläge"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryck snabbt två gånger bredvid en app för att flytta den"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dra till en annan app för läget Delad skärm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryck snabbt två gånger utanför en app för att flytta den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Utöka för mer information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Utöka"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Minimera"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Stäng"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Tillbaka"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handtag"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Datorläge"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Delad skärm"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Svävande"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index b3465ab1db85..7116ac162fbd 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bild-i-bild"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Stäng"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
- <string name="pip_move" msgid="1544227837964635439">"Flytta BIB"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Utöka bild-i-bild"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Komprimera bild-i-bild"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Tryck snabbt två gånger på "<annotation icon="home_icon">" HEM "</annotation>" för kontroller"</string>
+ <string name="pip_move" msgid="158770205886688553">"Flytta"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Utöka"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Komprimera"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Tryck snabbt två gånger på "<annotation icon="home_icon">"HEM"</annotation>" för inställningar"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Bild-i-bild-meny."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flytta åt vänster"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flytta åt höger"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Flytta uppåt"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Flytta nedåt"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index f6e558527ee5..345fbf81cd48 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Mipangilio"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Weka skrini iliyogawanywa"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menyu ya kipengele cha Kupachika Picha ndani ya Picha nyingine."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> iko katika hali ya picha ndani ya picha nyingine"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> itumie kipengele hiki, gusa ili ufungue mipangilio na uizime."</string>
<string name="pip_play" msgid="3496151081459417097">"Cheza"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Programu hii inaweza kufunguliwa katika dirisha 1 pekee."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programu hii haiwezi kufunguliwa kwenye madirisha mengine."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Kitenganishi cha skrini inayogawanywa"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Kitenganishi cha kugawa skrini"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrini nzima ya kushoto"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kushoto 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kushoto 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Baadhi ya programu hufanya kazi vizuri zaidi zikiwa wima"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Jaribu moja kati ya chaguo hizi ili utumie nafasi ya skrini yako kwa ufanisi"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungusha kifaa chako ili uende kwenye hali ya skrini nzima"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Gusa mara mbili karibu na programu ili uihamishe"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Buruta ndani programu nyingine ili utumie hali ya skrini iliyogawanywa"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Gusa mara mbili nje ya programu ili uihamishe"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Panua ili upate maelezo zaidi."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Panua"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Punguza"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Funga"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Rudi nyuma"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Ncha"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Hali ya Kompyuta ya mezani"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Gawa Skrini"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Zaidi"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index baff49ed821a..1e9406f01433 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pachika Picha Ndani ya Picha Nyingine"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Funga"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
- <string name="pip_move" msgid="1544227837964635439">"Kuhamisha PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Panua PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Kunja PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Bonyeza mara mbili kitufe cha "<annotation icon="home_icon">" UKURASA WA KWANZA "</annotation>" kupata vidhibiti"</string>
+ <string name="pip_move" msgid="158770205886688553">"Hamisha"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Panua"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Kunja"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Bonyeza mara mbili kitufe cha "<annotation icon="home_icon">" UKURASA WA KWANZA "</annotation>" kupata vidhibiti"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menyu ya kipengele cha kupachika picha ndani ya picha nyingine."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sogeza kushoto"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sogeza kulia"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Sogeza juu"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Sogeza chini"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Imemaliza"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index d8334adfe5ef..525f2ea043fc 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"அமைப்புகள்"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"திரைப் பிரிப்பு பயன்முறைக்குச் செல்"</string>
<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_play" msgid="3496151081459417097">"இயக்கு"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"திரைப் பிரிப்பு அம்சத்தில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</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="703810061635792791">"திரையைப் பிரிக்கும் பிரிப்பான்"</string>
+ <string name="divider_title" msgid="5482989479865361192">"திரைப் பிரிப்பான்"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்."</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="6688664582871779215">"சில ஆப்ஸ் \'போர்ட்ரெய்ட்டில்\' சிறப்பாகச் செயல்படும்"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ஸ்பேஸ்களிலிருந்து அதிகப் பலன்களைப் பெற இந்த விருப்பங்களில் ஒன்றைப் பயன்படுத்துங்கள்"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"முழுத்திரைக்குச் செல்ல உங்கள் சாதனத்தைச் சுழற்றவும்"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ஆப்ஸை இடம் மாற்ற, ஆப்ஸுக்கு அடுத்து இருமுறை தட்டவும்"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</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="maximize_button_text" msgid="1650859196290301963">"பெரிதாக்கும்"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"சிறிதாக்கும்"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"மூடும்"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"பின்செல்லும்"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ஹேண்டில்"</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="more_button_text" msgid="3655388105592893530">"கூடுதல் விருப்பத்தேர்வுகள்"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"மிதக்கும் சாளரம்"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index 4439e299c919..ef1bcf913eaf 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"பிக்ச்சர்-இன்-பிக்ச்சர்"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
+ <string name="pip_close" msgid="2955969519031223530">"மூடுக"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIPபை நகர்த்து"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIPபை விரிவாக்கு"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIPபைச் சுருக்கு"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" கட்டுப்பாடுகள்: "<annotation icon="home_icon">" முகப்பு "</annotation>" பட்டனை இருமுறை அழுத்துக"</string>
+ <string name="pip_move" msgid="158770205886688553">"நகர்த்து"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"விரி"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"சுருக்கு"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"கட்டுப்பாடுகளுக்கு "<annotation icon="home_icon">"முகப்பு"</annotation>" பட்டனை இருமுறை அழுத்துக"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"பிக்ச்சர்-இன்-பிக்ச்சர் மெனு."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"இடப்புறம் நகர்த்து"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"வலப்புறம் நகர்த்து"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"மேலே நகர்த்து"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"கீழே நகர்த்து"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"முடிந்தது"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 733075550007..0c0114afeed2 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -22,20 +22,23 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"సెట్టింగ్‌లు"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"స్ప్లిట్ స్క్రీన్‌ను ఎంటర్ చేయండి"</string>
<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_play" msgid="3496151081459417097">"ప్లే చేయి"</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>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"సైజ్‌ మార్చు"</string>
<string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</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="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
+ <string name="divider_title" msgid="5482989479865361192">"స్ప్లిట్ స్క్రీన్ డివైడర్"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"మెరుగైన వీక్షణ కోసం ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి."</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="6688664582871779215">"కొన్ని యాప్‌లు పోర్ట్రెయిట్‌లో ఉత్తమంగా పని చేస్తాయి"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"మీ ప్రదేశాన్ని ఎక్కువగా ఉపయోగించుకోవడానికి ఈ ఆప్షన్‌లలో ఒకదాన్ని ట్రై చేయండి"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ఫుల్ స్క్రీన్‌కు వెళ్లడానికి మీ పరికరాన్ని తిప్పండి"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"యాప్ స్థానాన్ని మార్చడానికి దాని పక్కన డబుల్-ట్యాప్ చేయండి"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"స్ప్లిట్-స్క్రీన్ కోసం మరొక యాప్‌లోకి లాగండి"</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="maximize_button_text" msgid="1650859196290301963">"గరిష్టీకరించండి"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"కుదించండి"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"మూసివేయండి"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"వెనుకకు"</string>
+ <string name="handle_text" msgid="1766582106752184456">"హ్యాండిల్"</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="more_button_text" msgid="3655388105592893530">"మరిన్ని"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ఫ్లోట్"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 35579346615f..d9237dff6dd8 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"పిక్చర్-ఇన్-పిక్చర్"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
+ <string name="pip_close" msgid="2955969519031223530">"మూసివేయండి"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ఫుల్-స్క్రీన్‌"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIPను తరలించండి"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIPని విస్తరించండి"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIPని కుదించండి"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" కంట్రోల్స్ కోసం "<annotation icon="home_icon">" HOME "</annotation>" బటన్ రెండుసార్లు నొక్కండి"</string>
+ <string name="pip_move" msgid="158770205886688553">"తరలించండి"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"విస్తరించండి"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"కుదించండి"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"కంట్రోల్స్ కోసం "<annotation icon="home_icon">"HOME"</annotation>" బటన్ రెండుసార్లు నొక్కండి"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"పిక్చర్-ఇన్-పిక్చర్ మెనూ."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ఎడమ వైపుగా జరపండి"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"కుడి వైపుగా జరపండి"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"పైకి జరపండి"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"కిందికి జరపండి"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"పూర్తయింది"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-television/dimen.xml b/libs/WindowManager/Shell/res/values-television/dimen.xml
index 14e89f8b08df..376cc4faca6f 100644
--- a/libs/WindowManager/Shell/res/values-television/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-television/dimen.xml
@@ -21,4 +21,7 @@
<!-- Padding between PIP and keep clear areas that caused it to move. -->
<dimen name="pip_keep_clear_area_padding">16dp</dimen>
+
+ <!-- The corner radius for PiP window. -->
+ <dimen name="pip_corner_radius">0dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index cfee8ea3242e..9f3a14610225 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"เข้าสู่โหมดแบ่งหน้าจอ"</string>
<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_play" msgid="3496151081459417097">"เล่น"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</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="703810061635792791">"เส้นแบ่งหน้าจอ"</string>
+ <string name="divider_title" msgid="5482989479865361192">"เส้นแยกหน้าจอ"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</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="6688664582871779215">"บางแอปทำงานได้ดีที่สุดในแนวตั้ง"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ลองใช้หนึ่งในตัวเลือกเหล่านี้เพื่อให้ได้ประโยชน์สูงสุดจากพื้นที่ว่าง"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"หมุนอุปกรณ์ให้แสดงเต็มหน้าจอ"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"แตะสองครั้งข้างแอปเพื่อเปลี่ยนตำแหน่ง"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</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="maximize_button_text" msgid="1650859196290301963">"ขยายใหญ่สุด"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"ย่อ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ปิด"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"กลับ"</string>
+ <string name="handle_text" msgid="1766582106752184456">"แฮนเดิล"</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="more_button_text" msgid="3655388105592893530">"เพิ่มเติม"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"ล่องลอย"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
index 0a07d157ec6f..47a6bd1be812 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"การแสดงภาพซ้อนภาพ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
- <string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"ปิด"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
- <string name="pip_move" msgid="1544227837964635439">"ย้าย PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"ขยาย PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"ยุบ PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" กดปุ่ม "<annotation icon="home_icon">" หน้าแรก "</annotation>" สองครั้งเพื่อเปิดการควบคุม"</string>
+ <string name="pip_move" msgid="158770205886688553">"ย้าย"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ขยาย"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ยุบ"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"กดปุ่ม "<annotation icon="home_icon">" หน้าแรก "</annotation>" สองครั้งเพื่อเปิดการควบคุม"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"เมนูการแสดงภาพซ้อนภาพ"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ย้ายไปทางซ้าย"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ย้ายไปทางขวา"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ย้ายขึ้น"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ย้ายลง"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"เสร็จ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index eed624dd5069..c20a07f1cf90 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Pumasok sa split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu ng Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Kung ayaw mong magamit ni <xliff:g id="NAME">%s</xliff:g> ang feature na ito, i-tap upang buksan ang mga setting at i-off ito."</string>
<string name="pip_play" msgid="3496151081459417097">"I-play"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Sa 1 window lang puwedeng buksan ang app na ito."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divider ng split-screen"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Divider ng split-screen"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"I-full screen ang nasa kaliwa"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Gawing 70% ang nasa kaliwa"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Gawing 50% ang nasa kaliwa"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"May ilang app na pinakamainam gamitin nang naka-portrait"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Subukan ang isa sa mga opsyong ito para masulit ang iyong space"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"I-rotate ang iyong device para mag-full screen"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Mag-double tap sa tabi ng isang app para iposisyon ito ulit"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Mag-drag ng ibang app para sa split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Mag-double tap sa labas ng app para baguhin ang posisyon nito"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"I-expand para sa higit pang impormasyon."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"I-maximize"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"I-minimize"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Isara"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Bumalik"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Higit pa"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index 9a11a38fa492..2d890d4126b6 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Walang pamagat na programa)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Isara ang PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Isara"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <string name="pip_move" msgid="1544227837964635439">"Ilipat ang PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"I-expand ang PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"I-collapse ang PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" I-double press ang "<annotation icon="home_icon">" HOME "</annotation>" para sa mga kontrol"</string>
+ <string name="pip_move" msgid="158770205886688553">"Ilipat"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"I-expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"I-collapse"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"I-double press ang "<annotation icon="home_icon">"HOME"</annotation>" para sa mga kontrol"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu ng Picture-in-Picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Ilipat pakaliwa"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Ilipat pakanan"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Itaas"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Ibaba"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Tapos na"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 2b4a2d0550f0..aeb86da2a6ed 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Bölünmüş ekrana geç"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Pencere içinde pencere menüsü"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> uygulamasının bu özelliği kullanmasını istemiyorsanız dokunarak ayarları açın ve söz konusu özelliği kapatın."</string>
<string name="pip_play" msgid="3496151081459417097">"Oynat"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu uygulama yalnızca 1 pencerede açılabilir."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcı"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Bölünmüş ekran ayırıcı"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Solda tam ekran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Solda %70"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Solda %50"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bazı uygulamalar dikey modda en iyi performansı gösterir"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Alanınızı en verimli şekilde kullanmak için bu seçeneklerden birini deneyin"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana geçmek için cihazınızı döndürün"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Yeniden konumlandırmak için uygulamanın yanına iki kez dokunun"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Yeniden konumlandırmak için uygulamanın dışına iki kez dokunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Daha fazla bilgi için genişletin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Ekranı Kapla"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Küçült"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Kapat"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Geri"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Herkese açık kullanıcı adı"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Modu"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Daha Fazla"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index bf4bc6f1fff7..9e3e59b74c19 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pencere İçinde Pencere"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
- <string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Kapat"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIP\'yi taşı"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP penceresini genişlet"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP penceresini daralt"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Kontroller için "<annotation icon="home_icon">" ANA SAYFA "</annotation>"\'ya iki kez basın"</string>
+ <string name="pip_move" msgid="158770205886688553">"Taşı"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Genişlet"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Daralt"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Kontroller için "<annotation icon="home_icon">"ANA EKRAN"</annotation>" düğmesine iki kez basın"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Pencere içinde pencere menüsü."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola taşı"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa taşı"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Yukarı taşı"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Aşağı taşı"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Bitti"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 02e726fbc3bf..0b61d7a85d9e 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -15,20 +15,20 @@
limitations under the License.
-->
<resources>
- <!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="pip_menu_button_size">48dp</dimen>
- <dimen name="pip_menu_button_radius">20dp</dimen>
- <dimen name="pip_menu_icon_size">20dp</dimen>
- <dimen name="pip_menu_button_margin">4dp</dimen>
- <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
- <dimen name="pip_menu_border_width">4dp</dimen>
- <integer name="pip_menu_fade_animation_duration">500</integer>
+ <!-- The dimensions to use for tv window menu action buttons. -->
+ <dimen name="tv_window_menu_button_size">48dp</dimen>
+ <dimen name="tv_window_menu_button_radius">20dp</dimen>
+ <dimen name="tv_window_menu_icon_size">20dp</dimen>
+ <dimen name="tv_window_menu_button_margin">4dp</dimen>
+ <integer name="tv_window_menu_fade_animation_duration">500</integer>
<!-- The pip menu front border corner radius is 2dp smaller than
the background corner radius to hide the background from
showing through. -->
<dimen name="pip_menu_border_corner_radius">4dp</dimen>
<dimen name="pip_menu_background_corner_radius">6dp</dimen>
+ <dimen name="pip_menu_border_width">4dp</dimen>
<dimen name="pip_menu_outer_space">24dp</dimen>
+ <dimen name="pip_menu_button_start_end_offset">30dp</dimen>
<!-- outer space minus border width -->
<dimen name="pip_menu_outer_space_frame">20dp</dimen>
@@ -41,8 +41,10 @@
<dimen name="pip_menu_edu_text_view_height">24dp</dimen>
<dimen name="pip_menu_edu_text_home_icon">9sp</dimen>
<dimen name="pip_menu_edu_text_home_icon_outline">14sp</dimen>
- <integer name="pip_edu_text_show_duration_ms">10500</integer>
- <integer name="pip_edu_text_window_exit_animation_duration_ms">1000</integer>
- <integer name="pip_edu_text_view_exit_animation_duration_ms">300</integer>
+ <integer name="pip_edu_text_scroll_times">2</integer>
+ <integer name="pip_edu_text_non_scroll_show_duration">10500</integer>
+ <integer name="pip_edu_text_start_scroll_delay">2000</integer>
+ <integer name="pip_edu_text_window_exit_animation_duration">1000</integer>
+ <integer name="pip_edu_text_view_exit_animation_duration">300</integer>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index c3411a837c78..b589ed8c7739 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Налаштування"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Розділити екран"</string>
<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_play" msgid="3496151081459417097">"Відтворити"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Додаток може не працювати в режимі розділеного екрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Цей додаток можна відкрити лише в одному вікні."</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="703810061635792791">"Розділювач екрана"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Розділювач екрана"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду."</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="6688664582871779215">"Деякі додатки найкраще працюють у вертикальній орієнтації"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Щоб максимально ефективно використовувати місце на екрані, спробуйте виконати одну з наведених нижче дій"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Щоб перейти в повноекранний режим, поверніть пристрій"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Щоб перемістити додаток, двічі торкніться області поруч із ним"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Щоб перемістити додаток, двічі торкніться області поза ним"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Розгорніть, щоб дізнатися більше."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Збільшити"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Згорнути"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрити"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Маркер"</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="more_button_text" msgid="3655388105592893530">"Більше"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Плаваюче вікно"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
index 7e9f54e68f54..5edb26977fe7 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Картинка в картинці"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Закрити"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
- <string name="pip_move" msgid="1544227837964635439">"Перемістити картинку в картинці"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Розгорнути картинку в картинці"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Згорнути картинку в картинці"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Відкрити елементи керування: двічі натисніть "<annotation icon="home_icon">"HOME"</annotation></string>
+ <string name="pip_move" msgid="158770205886688553">"Перемістити"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Розгорнути"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Згорнути"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Відкрити елементи керування: двічі натисніть "<annotation icon="home_icon">"HOME"</annotation></string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню \"картинка в картинці\""</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Перемістити ліворуч"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Перемістити праворуч"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Перемістити вгору"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Перемістити вниз"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index a31c2be25643..81672bff7545 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"ترتیبات"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"اسپلٹ اسکرین تک رسائی"</string>
<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_play" msgid="3496151081459417097">"چلائیں"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</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="703810061635792791">"سپلٹ اسکرین تقسیم کار"</string>
+ <string name="divider_title" msgid="5482989479865361192">"اسپلٹ اسکرین ڈیوائیڈر"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں۔"</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="6688664582871779215">"کچھ ایپس پورٹریٹ میں بہترین کام کرتی ہیں"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"اپنی اسپیس کا زیادہ سے زیادہ فائدہ اٹھانے کے لیے ان اختیارات میں سے ایک کو آزمائیں"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"پوری اسکرین پر جانے کیلئے اپنا آلہ گھمائیں"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس کے آگے دو بار تھپتھپائیں"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</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="maximize_button_text" msgid="1650859196290301963">"بڑا کریں"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"چھوٹا کریں"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"بند کریں"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"پیچھے"</string>
+ <string name="handle_text" msgid="1766582106752184456">"ہینڈل"</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="more_button_text" msgid="3655388105592893530">"مزید"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"فلوٹ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index c2ef69ff1488..42b9564ff549 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر میں تصویر"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
+ <string name="pip_close" msgid="2955969519031223530">"بند کریں"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
- <string name="pip_move" msgid="1544227837964635439">"‏PIP کو منتقل کریں"</string>
- <string name="pip_expand" msgid="7605396312689038178">"‏PIP کو پھیلائیں"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"‏PIP کو سکیڑیں"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" کنٹرولز کے لیے "<annotation icon="home_icon">"ہوم "</annotation>" بٹن کو دو بار دبائیں"</string>
+ <string name="pip_move" msgid="158770205886688553">"منتقل کریں"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"پھیلائیں"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"سکیڑیں"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"کنٹرولز کے لیے "<annotation icon="home_icon">"ہوم "</annotation>" کو دو بار دبائیں"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"تصویر میں تصویر کا مینو۔"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"دائیں منتقل کریں"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"بائیں منتقل کریں"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"اوپر منتقل کریں"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"نیچے منتقل کریں"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ہو گیا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 2e3222560dde..d0384e944248 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Sozlamalar"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Ajratilgan ekranga kirish"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Tasvir ustida tasvir menyusi"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ilovasi uchun bu funksiyani sozlamalar orqali faolsizlantirish mumkin."</string>
<string name="pip_play" msgid="3496151081459417097">"Ijro"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu ilovani faqat 1 ta oynada ochish mumkin."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekranni ikkiga bo‘lish chizig‘i"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Ekranni ikkiga ajratish chizigʻi"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Chapda to‘liq ekran"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Chapda 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Chapda 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Ayrim ilovalar tik holatda ishlashga eng mos"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Muhitdan yanada samarali foydalanish uchun quyidagilardan birini sinang"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Butun ekranda ochish uchun qurilmani buring"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Qayta joylash uchun keyingi ilova ustiga ikki marta bosing"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Qayta joylash uchun ilova tashqarisiga ikki marta bosing"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Batafsil axborot olish uchun kengaytiring."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Yoyish"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Kichraytirish"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Yopish"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Orqaga"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Desktop rejimi"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Yana"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index 9ab95c80aa25..83fd8b4e5425 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Tasvir ustida tasvir"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Yopish"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
- <string name="pip_move" msgid="1544227837964635439">"PIPni siljitish"</string>
- <string name="pip_expand" msgid="7605396312689038178">"PIP funksiyasini yoyish"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"PIP funksiyasini yopish"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Boshqaruv uchun "<annotation icon="home_icon">"ASOSIY"</annotation>" tugmani ikki marta bosing"</string>
+ <string name="pip_move" msgid="158770205886688553">"Boshqa joyga olish"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Yoyish"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Yopish"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Boshqaruv uchun "<annotation icon="home_icon">"ASOSIY"</annotation>" tugmani ikki marta bosing"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Tasvir ustida tasvir menyusi."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Chapga olish"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Oʻngga olish"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Tepaga olish"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pastga olish"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Tayyor"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 8f3cffecc952..49986b591e28 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Cài đặt"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Truy cập chế độ chia đôi màn hình"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Trình đơn hình trong hình."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Nếu bạn không muốn <xliff:g id="NAME">%s</xliff:g> sử dụng tính năng này, hãy nhấn để mở cài đặt và tắt tính năng này."</string>
<string name="pip_play" msgid="3496151081459417097">"Phát"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ứng dụng này chỉ có thể mở 1 cửa sổ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bộ chia chia đôi màn hình"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Bộ chia màn hình"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Toàn màn hình bên trái"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Trái 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Trái 50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</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="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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Một số ứng dụng hoạt động tốt nhất ở chế độ dọc"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Hãy thử một trong các tuỳ chọn sau để tận dụng không gian"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xoay thiết bị để chuyển sang chế độ toàn màn hình"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Nhấn đúp vào bên cạnh ứng dụng để đặt lại vị trí"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Kéo vào một ứng dụng khác để chia đôi màn hình"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Nhấn đúp bên ngoài ứng dụng để đặt lại vị trí"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mở rộng để xem thêm thông tin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Phóng to"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Thu nhỏ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Đóng"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Quay lại"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Xử lý"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Chế độ máy tính"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index 146376d3cab6..986690f0444c 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Hình trong hình"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Đóng"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
- <string name="pip_move" msgid="1544227837964635439">"Di chuyển PIP (Ảnh trong ảnh)"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Mở rộng PIP (Ảnh trong ảnh)"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Thu gọn PIP (Ảnh trong ảnh)"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Nhấn đúp vào nút "<annotation icon="home_icon">" MÀN HÌNH CHÍNH "</annotation>" để mở trình đơn điều khiển"</string>
+ <string name="pip_move" msgid="158770205886688553">"Di chuyển"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Mở rộng"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Thu gọn"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Nhấn đúp vào nút "<annotation icon="home_icon">"MÀN HÌNH CHÍNH"</annotation>" để mở trình đơn điều khiển"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Trình đơn hình trong hình."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Di chuyển sang trái"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Di chuyển sang phải"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Di chuyển lên"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Di chuyển xuống"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Xong"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 19a9d371e435..acdb2523597d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"设置"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"进入分屏模式"</string>
<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_play" msgid="3496151081459417097">"播放"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</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="703810061635792791">"分屏分隔线"</string>
+ <string name="divider_title" msgid="5482989479865361192">"分屏分隔线"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"点按即可重启此应用,获得更好的视图体验。"</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="6688664582871779215">"某些应用在纵向模式下才能发挥最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"这些选项都有助于您最大限度地利用屏幕空间,不妨从中择一试试"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋转设备即可进入全屏模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在某个应用旁边连续点按两次,即可调整它的位置"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖入另一个应用,即可使用分屏模式"</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="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"关闭"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
+ <string name="handle_text" msgid="1766582106752184456">"处理"</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="more_button_text" msgid="3655388105592893530">"更多"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"悬浮"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index 55407d2c699d..4da96e89f136 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"画中画"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
- <string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
+ <string name="pip_close" msgid="2955969519031223530">"关闭"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
- <string name="pip_move" msgid="1544227837964635439">"移动画中画窗口"</string>
- <string name="pip_expand" msgid="7605396312689038178">"展开 PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"收起 PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按两次"<annotation icon="home_icon">"主屏幕"</annotation>"按钮可查看相关控件"</string>
+ <string name="pip_move" msgid="158770205886688553">"移动"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"展开"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"收起"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"按两次"<annotation icon="home_icon">"主屏幕"</annotation>"按钮可查看相关控件"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"画中画菜单。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左移"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右移"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"上移"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"下移"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完成"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 0c40e963f2e4..b1a957e5c5cf 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"進入分割螢幕"</string>
<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_play" msgid="3496151081459417097">"播放"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</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="703810061635792791">"分割畫面分隔線"</string>
+ <string name="divider_title" msgid="5482989479865361192">"分割螢幕分隔線"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗。"</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="6688664582871779215">"部分應用程式需要使用直向模式才能發揮最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請嘗試以下選項,充分運用螢幕的畫面空間"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕按兩下即可調整位置"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖入另一個應用程式即可分割螢幕"</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="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"返去"</string>
+ <string name="handle_text" msgid="1766582106752184456">"控點"</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="more_button_text" msgid="3655388105592893530">"更多"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
index 15e278d8ecc2..ce850ef3c675 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"畫中畫"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
- <string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"關閉"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
- <string name="pip_move" msgid="1544227837964635439">"移動畫中畫"</string>
- <string name="pip_expand" msgid="7605396312689038178">"展開畫中畫"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"收合畫中畫"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按兩下"<annotation icon="home_icon">" 主畫面按鈕"</annotation>"即可顯示控制項"</string>
+ <string name="pip_move" msgid="158770205886688553">"移動"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"展開"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"收合"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"按兩下"<annotation icon="home_icon">" 主畫面按鈕"</annotation>"即可存取控制項"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"畫中畫選單。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"向左移"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"向右移"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"向上移"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"向下移"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完成"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 8691352cf94a..bb3dba17abc7 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -22,6 +22,7 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"進入分割畫面"</string>
<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_play" msgid="3496151081459417097">"播放"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</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="703810061635792791">"分割畫面分隔線"</string>
+ <string name="divider_title" msgid="5482989479865361192">"分割畫面分隔線"</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>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"請輕觸並重新啟動此應用程式,取得更良好的觀看體驗。"</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="6688664582871779215">"某些應用程式在直向模式下才能發揮最佳效果"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請試試這裡的任一方式,以充分運用螢幕畫面的空間"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕觸兩下即可調整位置"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖進另一個應用程式即可使用分割畫面模式"</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="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
+ <string name="handle_text" msgid="1766582106752184456">"控點"</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="more_button_text" msgid="3655388105592893530">"更多"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
index 0b17b31d23d0..df870851e72b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"子母畫面"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
- <string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
+ <string name="pip_close" msgid="2955969519031223530">"關閉"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
- <string name="pip_move" msgid="1544227837964635439">"移動子母畫面"</string>
- <string name="pip_expand" msgid="7605396312689038178">"展開子母畫面"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"收合子母畫面"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" 按兩下"<annotation icon="home_icon">"主畫面按鈕"</annotation>"即可顯示控制選項"</string>
+ <string name="pip_move" msgid="158770205886688553">"移動"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"展開"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"收合"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"按兩下"<annotation icon="home_icon">"主畫面按鈕"</annotation>"即可存取控制選項"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"子母畫面選單。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"向左移動"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"向右移動"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"向上移動"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"向下移動"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完成"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 44ffbc6afa45..51a23ff64403 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -19,9 +19,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"Vala"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Nweba"</string>
- <string name="pip_phone_settings" msgid="5468987116750491918">"Izilungiselelo"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Amasethingi"</string>
<string name="pip_phone_enter_split" msgid="7042877263880641911">"Faka ukuhlukanisa isikrini"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Imenyu"</string>
+ <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Imenyu Yesithombe-Esithombeni"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"U-<xliff:g id="NAME">%s</xliff:g> ungaphakathi kwesithombe esiphakathi kwesithombe"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Uma ungafuni i-<xliff:g id="NAME">%s</xliff:g> ukuthi isebenzise lesi sici, thepha ukuze uvule izilungiselelo uphinde uyivale."</string>
<string name="pip_play" msgid="3496151081459417097">"Dlala"</string>
@@ -33,9 +34,11 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Le-app ingavulwa kuphela ewindini eli-1."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Isihlukanisi sokuhlukanisa isikrini"</string>
+ <string name="divider_title" msgid="5482989479865361192">"Isihlukanisi sokuhlukanisa isikrini"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Isikrini esigcwele esingakwesokunxele"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kwesokunxele ngo-70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kwesokunxele ngo-50%"</string>
@@ -72,13 +75,23 @@
<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="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono."</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>
- <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Amanye ama-app asebenza ngcono uma eme ngobude"</string>
- <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Zama enye yalezi zinketho ukuze usebenzise isikhala sakho ngokugcwele"</string>
- <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungezisa idivayisi yakho ukuze uye esikrinini esigcwele"</string>
- <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Thepha kabili eduze kwe-app ukuze uyimise kabusha"</string>
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Thepha kabili ngaphandle kwe-app ukuze uyimise kabusha"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Nweba ukuze uthole ulwazi olwengeziwe"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Khulisa"</string>
+ <string name="minimize_button_text" msgid="271592547935841753">"Nciphisa"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Vala"</string>
+ <string name="back_button_text" msgid="1469718707134137085">"Emuva"</string>
+ <string name="handle_text" msgid="1766582106752184456">"Isibambo"</string>
+ <string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string>
+ <string name="desktop_text" msgid="1077633567027630454">"Imodi Yedeskithophu"</string>
+ <string name="split_screen_text" msgid="1396336058129570886">"Hlukanisa isikrini"</string>
+ <string name="more_button_text" msgid="3655388105592893530">"Okwengeziwe"</string>
+ <string name="float_button_text" msgid="9221657008391364581">"Iflowuthi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index dad8c8128222..34cc8f1f1647 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -19,10 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Isithombe-esithombeni"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
- <string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Vala"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
- <string name="pip_move" msgid="1544227837964635439">"Hambisa i-PIP"</string>
- <string name="pip_expand" msgid="7605396312689038178">"Nweba i-PIP"</string>
- <string name="pip_collapse" msgid="5732233773786896094">"Goqa i-PIP"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Chofoza kabili "<annotation icon="home_icon">" IKHAYA"</annotation>" mayelana nezilawuli"</string>
+ <string name="pip_move" msgid="158770205886688553">"Hambisa"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Nweba"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Goqa"</string>
+ <string name="pip_edu_text" msgid="7930546669915337998">"Chofoza kabili "<annotation icon="home_icon">" IKHAYA"</annotation>" mayelana nezilawuli"</string>
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Imenyu yesithombe-esithombeni"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Yisa kwesokunxele"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Yisa kwesokudla"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Khuphula"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Yehlisa"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Kwenziwe"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index fa90fe36b545..e6933ca3fce6 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -15,14 +15,17 @@
~ limitations under the License.
-->
<resources>
- <color name="tv_pip_menu_icon_focused">#0E0E0F</color>
- <color name="tv_pip_menu_icon_unfocused">#F8F9FA</color>
- <color name="tv_pip_menu_icon_disabled">#80868B</color>
- <color name="tv_pip_menu_close_icon_bg_focused">#D93025</color>
- <color name="tv_pip_menu_close_icon_bg_unfocused">#D69F261F</color>
- <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
- <color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color>
+ <color name="tv_window_menu_icon_focused">#0E0E0F</color>
+ <color name="tv_window_menu_icon_unfocused">#F8F9FA</color>
+
+ <color name="tv_window_menu_icon_disabled">#80868B</color>
+ <color name="tv_window_menu_close_icon_bg_focused">#D93025</color>
+ <color name="tv_window_menu_close_icon_bg_unfocused">#D69F261F</color>
+ <color name="tv_window_menu_icon_bg_focused">#E8EAED</color>
+ <color name="tv_window_menu_icon_bg_unfocused">#990E0E0F</color>
+
<color name="tv_pip_menu_focus_border">#E8EAED</color>
+ <color name="tv_pip_menu_dim_layer">#990E0E0F</color>
<color name="tv_pip_menu_background">#1E232C</color>
<color name="tv_pip_edu_text">#99D2E3FC</color>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 25eddf834f3d..9490ddc73488 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -30,6 +30,9 @@
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string>
+ <!-- accessibility Title of menu shown over picture-in-picture [CHAR LIMIT=NONE] -->
+ <string name="pip_menu_accessibility_title">Picture-in-Picture Menu</string>
+
<!-- PiP BTW notification title. [CHAR LIMIT=50] -->
<string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string>
@@ -75,8 +78,10 @@
<!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
<string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string>
- <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
+ <!-- Accessibility label and window tile for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
<string name="accessibility_divider">Split-screen divider</string>
+ <!-- Accessibility window title for the split-screen divider window [CHAR LIMIT=NONE] -->
+ <string name="divider_title">Split-screen divider</string>
<!-- Accessibility action for moving docked stack divider to make the left screen full screen [CHAR LIMIT=NONE] -->
<string name="accessibility_action_divider_left_full">Left full screen</string>
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 2b7a13eac6ca..8f806cf56c9b 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -42,8 +42,8 @@
<!-- Educative text instructing the user to double press the HOME button to access the pip
controls menu [CHAR LIMIT=50] -->
- <string name="pip_edu_text"> Double press <annotation icon="home_icon"> HOME </annotation> for
- controls </string>
+ <string name="pip_edu_text">Double press <annotation icon="home_icon">HOME</annotation> for
+ controls</string>
<!-- Accessibility announcement when opening the PiP menu. [CHAR LIMIT=NONE] -->
<string name="a11y_pip_menu_entered">Picture-in-Picture menu.</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index e58e785850fa..065fd95b3ebc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -42,6 +42,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
+import android.window.ScreenCapture;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
@@ -471,7 +472,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
* Take a screenshot of a task.
*/
public void screenshotTask(RunningTaskInfo taskInfo, Rect crop,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> consumer) {
final TaskAppearedInfo info = mTasks.get(taskInfo.taskId);
if (info == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
new file mode 100644
index 000000000000..8dd1369ecbb2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "ironwood-postsubmit": [
+ {
+ "name": "WMShellFlickerTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 95a89b1d23ff..94e01e96730c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -108,7 +108,6 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
if (mTaskViewTransitions != null) {
mTaskViewTransitions.addTaskView(this);
}
- setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index a0dde6ad168d..2ec9e8b12fc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.animation;
+import android.graphics.Path;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
@@ -53,6 +54,11 @@ public class Interpolators {
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
/**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
* The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
* is disappearing e.g. when moving off screen.
*/
@@ -81,4 +87,14 @@ public class Interpolators {
public static final PathInterpolator DIM_INTERPOLATOR =
new PathInterpolator(.23f, .87f, .52f, -0.11f);
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
}
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
new file mode 100644
index 000000000000..36cf29a4c4f3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -0,0 +1,69 @@
+/*
+ * 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.back;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.NonNull;
+import android.graphics.Color;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+
+/**
+ * Controls background surface for the back animations
+ */
+public class BackAnimationBackground {
+ private static final int BACKGROUND_LAYER = -1;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private SurfaceControl mBackgroundSurface;
+
+ public BackAnimationBackground(RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ }
+
+ void ensureBackground(int color, @NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundSurface != null) {
+ return;
+ }
+
+ final float[] colorComponents = new float[] { Color.red(color) / 255.f,
+ Color.green(color) / 255.f, Color.blue(color) / 255.f };
+
+ final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
+ .setName("back-animation-background")
+ .setCallsite("BackAnimationBackground")
+ .setColorLayer();
+
+ mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
+ mBackgroundSurface = colorLayerBuilder.build();
+ transaction.setColor(mBackgroundSurface, colorComponents)
+ .setLayer(mBackgroundSurface, BACKGROUND_LAYER)
+ .show(mBackgroundSurface);
+ }
+
+ void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundSurface == null) {
+ return;
+ }
+
+ if (mBackgroundSurface.isValid()) {
+ transaction.remove(mBackgroundSurface);
+ }
+ mBackgroundSurface = null;
+ }
+}
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 cbcd9498fe55..57a0fd593551 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
@@ -16,9 +16,6 @@
package com.android.wm.shell.back;
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
@@ -27,11 +24,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
-import android.app.WindowConfiguration;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
-import android.hardware.HardwareBuffer;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Handler;
@@ -42,18 +37,20 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.util.Log;
+import android.util.SparseArray;
+import android.view.IRemoteAnimationRunner;
import android.view.IWindowFocusObserver;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.BackAnimationAdaptor;
+import android.window.BackAnimationAdapter;
import android.window.BackEvent;
+import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
+import android.window.IBackAnimationFinishedCallback;
import android.window.IBackAnimationRunner;
-import android.window.IBackNaviAnimationController;
import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
@@ -72,32 +69,28 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Controls the window animation run when a user initiates a back gesture.
*/
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
- private static final String TAG = "BackAnimationController";
+ private static final String TAG = "ShellBackPreview";
private static final int SETTING_VALUE_OFF = 0;
private static final int SETTING_VALUE_ON = 1;
public static final boolean IS_ENABLED =
SystemProperties.getInt("persist.wm.debug.predictive_back",
SETTING_VALUE_ON) == SETTING_VALUE_ON;
- /** Flag for U animation features */
+ /** Flag for U animation features */
public static boolean IS_U_ANIMATION_ENABLED =
SystemProperties.getInt("persist.wm.debug.predictive_back_anim",
SETTING_VALUE_OFF) == SETTING_VALUE_ON;
/** Predictive back animation developer option */
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
- // TODO (b/241808055) Find a appropriate time to remove during refactor
- private static final boolean USE_TRANSITION =
- SystemProperties.getInt("persist.wm.debug.predictive_back_ani_trans", 1) != 0;
/**
- * Max duration to wait for a transition to finish before accepting another gesture start
- * request.
+ * Max duration to wait for an animation to finish before triggering the real back.
*/
- private static final long MAX_TRANSITION_DURATION = 2000;
+ private static final long MAX_ANIMATION_DURATION = 2000;
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
- /** Tracks if an uninterruptible transition is in progress */
- private boolean mTransitionInProgress = false;
+ /** Tracks if an uninterruptible animation is in progress */
+ private boolean mPostCommitAnimationInProgress = false;
/** Tracks if we should start the back gesture on the next motion move event */
private boolean mShouldStartOnNextMoveEvent = false;
/** @see #setTriggerBack(boolean) */
@@ -105,28 +98,27 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@Nullable
private BackNavigationInfo mBackNavigationInfo;
- private final SurfaceControl.Transaction mTransaction;
private final IActivityTaskManager mActivityTaskManager;
private final Context mContext;
private final ContentResolver mContentResolver;
private final ShellController mShellController;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
- @Nullable
- private IOnBackInvokedCallback mBackToLauncherCallback;
- private float mTriggerThreshold;
- private final Runnable mResetTransitionRunnable = () -> {
- finishAnimation();
- mTransitionInProgress = false;
+ private final Runnable mAnimationTimeoutRunnable = () -> {
+ ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
+ MAX_ANIMATION_DURATION);
+ onBackAnimationFinished();
};
- private RemoteAnimationTarget mAnimationTarget;
- IBackAnimationRunner mIBackAnimationRunner;
- private IBackNaviAnimationController mBackAnimationController;
- private BackAnimationAdaptor mBackAnimationAdaptor;
+ private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
+ @VisibleForTesting
+ BackAnimationAdapter mBackAnimationAdapter;
private final TouchTracker mTouchTracker = new TouchTracker();
- private final CachingBackDispatcher mCachingBackDispatcher = new CachingBackDispatcher();
+
+ private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
+ @Nullable
+ private IOnBackInvokedCallback mActiveCallback;
@VisibleForTesting
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
@@ -135,70 +127,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@Override
public void focusLost(IBinder inputToken) {
mShellExecutor.execute(() -> {
- if (!mBackGestureStarted || mTransitionInProgress) {
- // If an uninterruptible transition is already in progress, we should ignore
- // this due to the transition may cause focus lost. (alpha = 0)
+ if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
+ // If an uninterruptible animation is already in progress, we should ignore
+ // this due to it may cause focus lost. (alpha = 0)
return;
}
+ ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
setTriggerBack(false);
onGestureFinished(false);
});
}
};
- /**
- * Cache the temporary callback and trigger result if gesture was finish before received
- * BackAnimationRunner#onAnimationStart/cancel, so there can continue play the animation.
- */
- private class CachingBackDispatcher {
- private IOnBackInvokedCallback mOnBackCallback;
- private boolean mTriggerBack;
- // Whether we are waiting to receive onAnimationStart
- private boolean mWaitingAnimation;
-
- void startWaitingAnimation() {
- mWaitingAnimation = true;
- }
-
- boolean set(IOnBackInvokedCallback callback, boolean triggerBack) {
- if (mWaitingAnimation) {
- mOnBackCallback = callback;
- mTriggerBack = triggerBack;
- return true;
- }
- return false;
- }
-
- boolean consume() {
- boolean consumed = false;
- if (mWaitingAnimation && mOnBackCallback != null) {
- if (mTriggerBack) {
- final BackEvent backFinish = mTouchTracker.createProgressEvent(1);
- dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
- dispatchOnBackInvoked(mOnBackCallback);
- } else {
- final BackEvent backFinish = mTouchTracker.createProgressEvent(0);
- dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
- dispatchOnBackCancelled(mOnBackCallback);
- }
- startTransition();
- consumed = true;
- }
- mOnBackCallback = null;
- mWaitingAnimation = false;
- return consumed;
- }
- }
+ private final BackAnimationBackground mAnimationBackground;
public BackAnimationController(
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
- Context context) {
+ Context context,
+ @NonNull BackAnimationBackground backAnimationBackground) {
this(shellInit, shellController, shellExecutor, backgroundHandler,
- new SurfaceControl.Transaction(), ActivityTaskManager.getService(),
- context, context.getContentResolver());
+ ActivityTaskManager.getService(), context, context.getContentResolver(),
+ backAnimationBackground);
}
@VisibleForTesting
@@ -207,17 +159,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@NonNull ShellController shellController,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler bgHandler,
- @NonNull SurfaceControl.Transaction transaction,
@NonNull IActivityTaskManager activityTaskManager,
- Context context, ContentResolver contentResolver) {
+ Context context, ContentResolver contentResolver,
+ @NonNull BackAnimationBackground backAnimationBackground) {
mShellController = shellController;
mShellExecutor = shellExecutor;
- mTransaction = transaction;
mActivityTaskManager = activityTaskManager;
mContext = context;
mContentResolver = contentResolver;
mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
+ mAnimationBackground = backAnimationBackground;
}
@VisibleForTesting
@@ -227,8 +179,27 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void onInit() {
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
+ 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);
+ final CrossActivityAnimation crossActivityAnimation =
+ new CrossActivityAnimation(mContext, mAnimationBackground);
+ mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
+ crossActivityAnimation.mBackAnimationRunner);
+ // TODO (236760237): register dialog close animation when it's completed.
}
private void setupAnimationDeveloperSettingsObserver(
@@ -253,8 +224,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
boolean isEnabled = settingValue == SETTING_VALUE_ON;
mEnableAnimations.set(isEnabled);
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s",
- isEnabled);
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
}
public BackAnimation getBackAnimationImpl() {
@@ -305,21 +275,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
@Override
- public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
+ IRemoteAnimationRunner runner) {
executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
- (controller) -> controller.setBackToLauncherCallback(callback));
+ (controller) -> controller.registerAnimation(
+ BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ new BackAnimationRunner(callback, runner)));
}
@Override
public void clearBackToLauncherCallback() {
executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
- (controller) -> controller.clearBackToLauncherCallback());
- }
-
- @Override
- public void onBackToLauncherAnimationFinished() {
- executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished",
- (controller) -> controller.onBackToLauncherAnimationFinished());
+ (controller) -> controller.unregisterAnimation(
+ BackNavigationInfo.TYPE_RETURN_TO_HOME));
}
@Override
@@ -328,32 +296,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
- @VisibleForTesting
- void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
- mBackToLauncherCallback = callback;
- if (USE_TRANSITION) {
- createAdaptor();
- }
+ void registerAnimation(@BackNavigationInfo.BackTargetType int type,
+ @NonNull BackAnimationRunner runner) {
+ mAnimationDefinition.set(type, runner);
}
- private void clearBackToLauncherCallback() {
- mBackToLauncherCallback = null;
- }
-
- @VisibleForTesting
- void onBackToLauncherAnimationFinished() {
- final boolean triggerBack = mTriggerBack;
- IOnBackInvokedCallback callback = mBackNavigationInfo != null
- ? mBackNavigationInfo.getOnBackInvokedCallback() : null;
- // Make sure the notification sequence should be controller > client.
- finishAnimation();
- if (callback != null) {
- if (triggerBack) {
- dispatchOnBackInvoked(callback);
- } else {
- dispatchOnBackCancelled(callback);
- }
- }
+ void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
+ mAnimationDefinition.remove(type);
}
/**
@@ -362,7 +311,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
*/
public void onMotionEvent(float touchX, float touchY, int keyAction,
@BackEvent.SwipeEdge int swipeEdge) {
- if (mTransitionInProgress) {
+ if (mPostCommitAnimationInProgress) {
return;
}
@@ -379,7 +328,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
onGestureStarted(touchX, touchY, swipeEdge);
mShouldStartOnNextMoveEvent = false;
}
- onMove(touchX, touchY, swipeEdge);
+ onMove();
} else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
"Finishing gesture with event action: %d", keyAction);
@@ -394,20 +343,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
if (mBackGestureStarted || mBackNavigationInfo != null) {
Log.e(TAG, "Animation is being initialized but is already started.");
- finishAnimation();
+ finishBackNavigation();
}
mTouchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
mBackGestureStarted = true;
try {
- boolean requestAnimation = mEnableAnimations.get();
- mBackNavigationInfo = mActivityTaskManager.startBackNavigation(requestAnimation,
- mFocusObserver, mBackAnimationAdaptor);
+ mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
+ mFocusObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
onBackNavigationInfoReceived(mBackNavigationInfo);
} catch (RemoteException remoteException) {
Log.e(TAG, "Failed to initAnimation", remoteException);
- finishAnimation();
+ finishBackNavigation();
}
}
@@ -417,82 +365,29 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
Log.e(TAG, "Received BackNavigationInfo is null.");
return;
}
- int backType = backNavigationInfo.getType();
- IOnBackInvokedCallback targetCallback = null;
- final boolean dispatchToLauncher = shouldDispatchToLauncher(backType);
- if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
- HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
- if (hardwareBuffer != null) {
- displayTargetScreenshot(hardwareBuffer,
- backNavigationInfo.getTaskWindowConfiguration());
- }
- targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
- mTransaction.apply();
- } else if (dispatchToLauncher) {
- targetCallback = mBackToLauncherCallback;
- if (USE_TRANSITION) {
- mCachingBackDispatcher.startWaitingAnimation();
+ final int backType = backNavigationInfo.getType();
+ final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
+ if (shouldDispatchToAnimator) {
+ if (mAnimationDefinition.contains(backType)) {
+ mActiveCallback = mAnimationDefinition.get(backType).getCallback();
+ mAnimationDefinition.get(backType).startGesture();
+ } else {
+ mActiveCallback = null;
}
- } else if (backType == BackNavigationInfo.TYPE_CALLBACK) {
- targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
- }
- if (!USE_TRANSITION || !dispatchToLauncher) {
- dispatchOnBackStarted(
- targetCallback,
- mTouchTracker.createStartEvent(
- mBackNavigationInfo.getDepartingAnimationTarget()));
+ } else {
+ mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
+ dispatchOnBackStarted(mActiveCallback, mTouchTracker.createStartEvent(null));
}
}
- /**
- * Display the screenshot of the activity beneath.
- *
- * @param hardwareBuffer The buffer containing the screenshot.
- */
- private void displayTargetScreenshot(@NonNull HardwareBuffer hardwareBuffer,
- WindowConfiguration taskWindowConfiguration) {
- SurfaceControl screenshotSurface =
- mBackNavigationInfo == null ? null : mBackNavigationInfo.getScreenshotSurface();
- if (screenshotSurface == null) {
- Log.e(TAG, "BackNavigationInfo doesn't contain a surface for the screenshot. ");
+ private void onMove() {
+ if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()
+ || mActiveCallback == null) {
return;
}
- // Scale the buffer to fill the whole Task
- float sx = 1;
- float sy = 1;
- float w = taskWindowConfiguration.getBounds().width();
- float h = taskWindowConfiguration.getBounds().height();
-
- if (w != hardwareBuffer.getWidth()) {
- sx = w / hardwareBuffer.getWidth();
- }
-
- if (h != hardwareBuffer.getHeight()) {
- sy = h / hardwareBuffer.getHeight();
- }
- mTransaction.setScale(screenshotSurface, sx, sy);
- mTransaction.setBuffer(screenshotSurface, hardwareBuffer);
- mTransaction.setVisibility(screenshotSurface, true);
- }
-
- private void onMove(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
- if (!mBackGestureStarted || mBackNavigationInfo == null) {
- return;
- }
- final BackEvent backEvent = mTouchTracker.createProgressEvent();
- if (USE_TRANSITION && mBackAnimationController != null && mAnimationTarget != null) {
- dispatchOnBackProgressed(mBackToLauncherCallback, backEvent);
- } else if (mEnableAnimations.get()) {
- int backType = mBackNavigationInfo.getType();
- IOnBackInvokedCallback targetCallback;
- if (shouldDispatchToLauncher(backType)) {
- targetCallback = mBackToLauncherCallback;
- } else {
- targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
- }
- dispatchOnBackProgressed(targetCallback, backEvent);
- }
+ final BackMotionEvent backEvent = mTouchTracker.createProgressEvent();
+ dispatchOnBackProgressed(mActiveCallback, backEvent);
}
private void injectBackKey() {
@@ -514,71 +409,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
- private void onGestureFinished(boolean fromTouch) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
- if (!mBackGestureStarted) {
- finishAnimation();
- return;
- }
-
- if (fromTouch) {
- // Let touch reset the flag otherwise it will start a new back navigation and refresh
- // the info when received a new move event.
- mBackGestureStarted = false;
- }
-
- if (mTransitionInProgress) {
- return;
- }
-
- if (mBackNavigationInfo == null) {
- // No focus window found or core are running recents animation, inject back key as
- // legacy behavior.
- if (mTriggerBack) {
- injectBackKey();
- }
- finishAnimation();
- return;
- }
-
- int backType = mBackNavigationInfo.getType();
- boolean shouldDispatchToLauncher = shouldDispatchToLauncher(backType);
- IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
- ? mBackToLauncherCallback
- : mBackNavigationInfo.getOnBackInvokedCallback();
- if (mCachingBackDispatcher.set(targetCallback, mTriggerBack)) {
- return;
- }
- if (shouldDispatchToLauncher) {
- startTransition();
- }
- if (mTriggerBack) {
- dispatchOnBackInvoked(targetCallback);
- } else {
- dispatchOnBackCancelled(targetCallback);
- }
- if (backType != BackNavigationInfo.TYPE_RETURN_TO_HOME || !shouldDispatchToLauncher) {
- // Launcher callback missing. Simply finish animation.
- finishAnimation();
- }
- }
-
- private boolean shouldDispatchToLauncher(int backType) {
- return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
- && mBackToLauncherCallback != null
- && mEnableAnimations.get()
+ private boolean shouldDispatchToAnimator() {
+ return mEnableAnimations.get()
&& mBackNavigationInfo != null
- && ((USE_TRANSITION && mBackNavigationInfo.isPrepareRemoteAnimation())
- || mBackNavigationInfo.getDepartingAnimationTarget() != null);
+ && mBackNavigationInfo.isPrepareRemoteAnimation();
}
private void dispatchOnBackStarted(IOnBackInvokedCallback callback,
- BackEvent backEvent) {
+ BackMotionEvent backEvent) {
if (callback == null) {
return;
}
try {
- if (shouldDispatchAnimation(callback)) {
+ if (mEnableAnimations.get()) {
callback.onBackStarted(backEvent);
}
} catch (RemoteException e) {
@@ -602,7 +445,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
try {
- if (shouldDispatchAnimation(callback)) {
+ if (mEnableAnimations.get()) {
callback.onBackCancelled();
}
} catch (RemoteException e) {
@@ -611,12 +454,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
- BackEvent backEvent) {
+ BackMotionEvent backEvent) {
if (callback == null) {
return;
}
try {
- if (shouldDispatchAnimation(callback)) {
+ if (mEnableAnimations.get()) {
callback.onBackProgressed(backEvent);
}
} catch (RemoteException e) {
@@ -625,15 +468,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private boolean shouldDispatchAnimation(IOnBackInvokedCallback callback) {
- return (IS_U_ANIMATION_ENABLED || callback == mBackToLauncherCallback)
- && mEnableAnimations.get();
+ // TODO(b/258698745): Only dispatch to animation callbacks.
+ return mEnableAnimations.get();
}
/**
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/
public void setTriggerBack(boolean triggerBack) {
- if (mTransitionInProgress) {
+ if (mPostCommitAnimationInProgress) {
return;
}
mTriggerBack = triggerBack;
@@ -642,102 +485,177 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void setSwipeThresholds(float triggerThreshold, float progressThreshold) {
mTouchTracker.setProgressThreshold(progressThreshold);
- mTriggerThreshold = triggerThreshold;
}
- private void finishAnimation() {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
- mTouchTracker.reset();
- BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
- boolean triggerBack = mTriggerBack;
- mBackNavigationInfo = null;
- mAnimationTarget = null;
- mTriggerBack = false;
- mShouldStartOnNextMoveEvent = false;
- if (backNavigationInfo == null) {
- return;
+ private void invokeOrCancelBack() {
+ // Make a synchronized call to core before dispatch back event to client side.
+ // If the close transition happens before the core receives onAnimationFinished, there will
+ // play a second close animation for that transition.
+ if (mBackAnimationFinishedCallback != null) {
+ try {
+ mBackAnimationFinishedCallback.onAnimationFinished(mTriggerBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
+ }
+ mBackAnimationFinishedCallback = null;
}
- if (!USE_TRANSITION) {
- RemoteAnimationTarget animationTarget = backNavigationInfo
- .getDepartingAnimationTarget();
- if (animationTarget != null) {
- if (animationTarget.leash != null && animationTarget.leash.isValid()) {
- mTransaction.remove(animationTarget.leash);
- }
- }
- SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
- if (screenshotSurface != null && screenshotSurface.isValid()) {
- mTransaction.remove(screenshotSurface);
+ if (mBackNavigationInfo != null) {
+ final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
+ if (mTriggerBack) {
+ dispatchOnBackInvoked(callback);
+ } else {
+ dispatchOnBackCancelled(callback);
}
- mTransaction.apply();
- }
- stopTransition();
- backNavigationInfo.onBackNavigationFinished(triggerBack);
- if (USE_TRANSITION) {
- final IBackNaviAnimationController controller = mBackAnimationController;
- if (controller != null) {
- try {
- controller.finish(triggerBack);
- } catch (RemoteException r) {
- // Oh no!
- }
+ }
+ finishBackNavigation();
+ }
+
+ /**
+ * Called when the gesture is released, then it could start the post commit animation.
+ */
+ private void onGestureFinished(boolean fromTouch) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+ if (!mBackGestureStarted) {
+ finishBackNavigation();
+ return;
+ }
+
+ if (fromTouch) {
+ // Let touch reset the flag otherwise it will start a new back navigation and refresh
+ // the info when received a new move event.
+ mBackGestureStarted = false;
+ }
+
+ if (mPostCommitAnimationInProgress) {
+ ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
+ return;
+ }
+
+ if (mBackNavigationInfo == null) {
+ // No focus window found or core are running recents animation, inject back key as
+ // legacy behavior.
+ if (mTriggerBack) {
+ injectBackKey();
}
- mBackAnimationController = null;
+ finishBackNavigation();
+ return;
+ }
+
+ final int backType = mBackNavigationInfo.getType();
+ // Simply trigger and finish back navigation when no animator defined.
+ if (!shouldDispatchToAnimator() || mActiveCallback == null) {
+ invokeOrCancelBack();
+ return;
+ }
+
+ final BackAnimationRunner runner = mAnimationDefinition.get(backType);
+ if (runner.isWaitingAnimation()) {
+ ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
+ return;
}
+ startPostCommitAnimation();
}
- private void startTransition() {
- if (mTransitionInProgress) {
+ /**
+ * Start the phase 2 animation when gesture is released.
+ * Callback to {@link #onBackAnimationFinished} when it is finished or timeout.
+ */
+ private void startPostCommitAnimation() {
+ if (mPostCommitAnimationInProgress) {
return;
}
- mTransitionInProgress = true;
- mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()");
+ mPostCommitAnimationInProgress = true;
+ mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
+
+ // The next callback should be {@link #onBackAnimationFinished}.
+ if (mTriggerBack) {
+ dispatchOnBackInvoked(mActiveCallback);
+ } else {
+ dispatchOnBackCancelled(mActiveCallback);
+ }
}
- private void stopTransition() {
- if (!mTransitionInProgress) {
+ /**
+ * Called when the post commit animation is completed or timeout.
+ * This will trigger the real {@link IOnBackInvokedCallback} behavior.
+ */
+ @VisibleForTesting
+ void onBackAnimationFinished() {
+ if (!mPostCommitAnimationInProgress) {
return;
}
- mShellExecutor.removeCallbacks(mResetTransitionRunnable);
- mTransitionInProgress = false;
+ // Stop timeout runner.
+ mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
+ mPostCommitAnimationInProgress = false;
+
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
+
+ // Trigger the real back.
+ invokeOrCancelBack();
}
- private void createAdaptor() {
- mIBackAnimationRunner = new IBackAnimationRunner.Stub() {
+ /**
+ * This should be called after the whole back navigation is completed.
+ */
+ @VisibleForTesting
+ void finishBackNavigation() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
+ mShouldStartOnNextMoveEvent = false;
+ mTouchTracker.reset();
+ mActiveCallback = null;
+ if (mBackNavigationInfo != null) {
+ mBackNavigationInfo.onBackNavigationFinished(mTriggerBack);
+ mBackNavigationInfo = null;
+ }
+ mTriggerBack = false;
+ }
+
+ private void createAdapter() {
+ IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
@Override
- public void onAnimationCancelled() {
- // no op for now
- }
- @Override // Binder interface
- public void onAnimationStart(IBackNaviAnimationController controller, int type,
- RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps) {
+ public void onAnimationStart(int type, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IBackAnimationFinishedCallback finishedCallback) {
mShellExecutor.execute(() -> {
- mBackAnimationController = controller;
- for (int i = 0; i < apps.length; i++) {
- final RemoteAnimationTarget target = apps[i];
- if (MODE_CLOSING == target.mode) {
- mAnimationTarget = target;
- } else if (MODE_OPENING == target.mode) {
- // TODO Home activity should handle the visibility for itself
- // once it finish relayout for orientation change
- SurfaceControl.Transaction tx =
- new SurfaceControl.Transaction();
- tx.setAlpha(target.leash, 1);
- tx.apply();
+ final BackAnimationRunner runner = mAnimationDefinition.get(type);
+ 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;
}
- dispatchOnBackStarted(mBackToLauncherCallback,
- mTouchTracker.createStartEvent(mAnimationTarget));
- final BackEvent backInit = mTouchTracker.createProgressEvent();
- if (!mCachingBackDispatcher.consume()) {
- dispatchOnBackProgressed(mBackToLauncherCallback, backInit);
+ 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]));
+ }
+
+ if (!mBackGestureStarted) {
+ // if the down -> up gesture happened before animation start, we have to
+ // trigger the uninterruptible transition to finish the back animation.
+ final BackMotionEvent backFinish = mTouchTracker.createProgressEvent();
+ dispatchOnBackProgressed(mActiveCallback, backFinish);
+ startPostCommitAnimation();
}
});
}
+
+ @Override
+ public void onAnimationCancelled() { }
};
- mBackAnimationAdaptor = new BackAnimationAdaptor(mIBackAnimationRunner,
- BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ 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
new file mode 100644
index 000000000000..d70b8f53a911
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -0,0 +1,89 @@
+/*
+ * 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.back;
+
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.window.IBackAnimationRunner;
+import android.window.IOnBackInvokedCallback;
+
+/**
+ * Used to register the animation callback and runner, it will trigger result if gesture was finish
+ * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue
+ * trigger the real back behavior.
+ */
+class BackAnimationRunner {
+ private static final String TAG = "ShellBackPreview";
+
+ private final IOnBackInvokedCallback mCallback;
+ private final IRemoteAnimationRunner mRunner;
+
+ // Whether we are waiting to receive onAnimationStart
+ private boolean mWaitingAnimation;
+
+ BackAnimationRunner(@NonNull IOnBackInvokedCallback callback,
+ @NonNull IRemoteAnimationRunner runner) {
+ mCallback = callback;
+ mRunner = runner;
+ }
+
+ /** Returns the registered animation runner */
+ IRemoteAnimationRunner getRunner() {
+ return mRunner;
+ }
+
+ /** Returns the registered animation callback */
+ IOnBackInvokedCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Called from {@link IBackAnimationRunner}, it will deliver these
+ * {@link RemoteAnimationTarget}s to the corresponding runner.
+ */
+ void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, Runnable finishedCallback) {
+ final IRemoteAnimationFinishedCallback callback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() {
+ finishedCallback.run();
+ }
+ };
+ mWaitingAnimation = false;
+ try {
+ mRunner.onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers,
+ nonApps, callback);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onAnimationStart", e);
+ }
+ }
+
+ void startGesture() {
+ mWaitingAnimation = true;
+ }
+
+ boolean isWaitingAnimation() {
+ return mWaitingAnimation;
+ }
+}
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
new file mode 100644
index 000000000000..5d384944821c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -0,0 +1,373 @@
+/*
+ * 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.back;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.window.BackEvent;
+import android.window.BackMotionEvent;
+import android.window.BackProgressAnimator;
+import android.window.IOnBackInvokedCallback;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/** Class that defines cross-activity animation. */
+@ShellMainThread
+class CrossActivityAnimation {
+ /**
+ * Minimum scale of the entering/closing window.
+ */
+ private static final float MIN_WINDOW_SCALE = 0.9f;
+
+ /**
+ * Minimum alpha of the closing/entering window.
+ */
+ private static final float CLOSING_MIN_WINDOW_ALPHA = 0.5f;
+
+ /**
+ * Progress value to fly out closing window and fly in entering window.
+ */
+ private static final float SWITCH_ENTERING_WINDOW_PROGRESS = 0.5f;
+
+ /** Max window translation in the Y axis. */
+ private static final int WINDOW_MAX_DELTA_Y = 160;
+
+ /** Duration of fade in/out entering window. */
+ private static final int FADE_IN_DURATION = 100;
+ /** Duration of post animation after gesture committed. */
+ private static final int POST_ANIMATION_DURATION = 350;
+ private static final Interpolator INTERPOLATOR = Interpolators.EMPHASIZED;
+
+ private final Rect mStartTaskRect = new Rect();
+ private final float mCornerRadius;
+
+ // The closing window properties.
+ private final RectF mClosingRect = new RectF();
+
+ // The entering window properties.
+ private final Rect mEnteringStartRect = new Rect();
+ private final RectF mEnteringRect = new RectF();
+
+ private float mCurrentAlpha = 1.0f;
+
+ private float mEnteringMargin = 0;
+ private ValueAnimator mEnteringAnimator;
+ private boolean mEnteringWindowShow = false;
+
+ private final PointF mInitialTouchPos = new PointF();
+
+ private final Matrix mTransformMatrix = new Matrix();
+
+ private final float[] mTmpFloat9 = new float[9];
+
+ private RemoteAnimationTarget mEnteringTarget;
+ private RemoteAnimationTarget mClosingTarget;
+ private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ private boolean mBackInProgress = false;
+
+ private PointF mTouchPos = new PointF();
+ private IRemoteAnimationFinishedCallback mFinishCallback;
+
+ private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
+ final BackAnimationRunner mBackAnimationRunner;
+
+ private final BackAnimationBackground mBackground;
+
+ CrossActivityAnimation(Context context, BackAnimationBackground background) {
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
+ mBackground = background;
+ }
+
+ private static float mapRange(float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
+ private float getInterpolatedProgress(float backProgress) {
+ return INTERPOLATOR.getInterpolation(backProgress);
+ }
+
+ private void startBackAnimation() {
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
+ return;
+ }
+ mTransaction.setAnimationTransaction();
+
+ // Offset start rectangle to align task bounds.
+ mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds());
+ mStartTaskRect.offsetTo(0, 0);
+
+ // Draw background with task background color.
+ mBackground.ensureBackground(
+ mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
+ }
+
+ private void applyTransform(SurfaceControl leash, RectF targetRect, float targetAlpha) {
+ final float scale = targetRect.width() / mStartTaskRect.width();
+ mTransformMatrix.reset();
+ mTransformMatrix.setScale(scale, scale);
+ mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
+ mTransaction.setAlpha(leash, targetAlpha)
+ .setMatrix(leash, mTransformMatrix, mTmpFloat9)
+ .setWindowCrop(leash, mStartTaskRect)
+ .setCornerRadius(leash, mCornerRadius);
+ }
+
+ private void finishAnimation() {
+ if (mEnteringTarget != null) {
+ mEnteringTarget.leash.release();
+ mEnteringTarget = null;
+ }
+ if (mClosingTarget != null) {
+ mClosingTarget.leash.release();
+ mClosingTarget = null;
+ }
+ if (mBackground != null) {
+ mBackground.removeBackground(mTransaction);
+ }
+
+ mTransaction.apply();
+ mBackInProgress = false;
+ mTransformMatrix.reset();
+ mInitialTouchPos.set(0, 0);
+ mEnteringWindowShow = false;
+ mEnteringMargin = 0;
+ mEnteringAnimator = null;
+
+ if (mFinishCallback != null) {
+ try {
+ mFinishCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ mFinishCallback = null;
+ }
+ }
+
+ private void onGestureProgress(@NonNull BackEvent backEvent) {
+ if (!mBackInProgress) {
+ mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ mBackInProgress = true;
+ }
+ mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ return;
+ }
+
+ final float progress = getInterpolatedProgress(backEvent.getProgress());
+ final float touchY = mTouchPos.y;
+
+ final int width = mStartTaskRect.width();
+ final int height = mStartTaskRect.height();
+
+ final float closingScale = mapRange(progress, 1, MIN_WINDOW_SCALE);
+
+ final float closingWidth = closingScale * width;
+ final float closingHeight = (float) height / width * closingWidth;
+
+ // Move the window along the X axis.
+ final float closingLeft = mStartTaskRect.left + (width - closingWidth) / 2;
+
+ // Move the window along the Y axis.
+ final float deltaYRatio = (touchY - mInitialTouchPos.y) / height;
+ final float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * WINDOW_MAX_DELTA_Y;
+ final float closingTop = (height - closingHeight) * 0.5f + deltaY;
+ mClosingRect.set(
+ closingLeft, closingTop, closingLeft + closingWidth, closingTop + closingHeight);
+ mEnteringRect.set(mClosingRect);
+
+ // Switch closing/entering targets while reach to the threshold progress.
+ if (showEnteringWindow(progress > SWITCH_ENTERING_WINDOW_PROGRESS)) {
+ return;
+ }
+
+ // Present windows and update the alpha.
+ mCurrentAlpha = Math.max(mapRange(progress, 1.0f, 0), CLOSING_MIN_WINDOW_ALPHA);
+ mClosingRect.offset(mEnteringMargin, 0);
+ mEnteringRect.offset(mEnteringMargin - width, 0);
+
+ applyTransform(
+ mClosingTarget.leash, mClosingRect, mEnteringWindowShow ? 0.01f : mCurrentAlpha);
+ applyTransform(
+ mEnteringTarget.leash, mEnteringRect, mEnteringWindowShow ? mCurrentAlpha : 0.01f);
+ mTransaction.apply();
+ }
+
+ private boolean showEnteringWindow(boolean show) {
+ if (mEnteringAnimator == null) {
+ mEnteringAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(FADE_IN_DURATION);
+ mEnteringAnimator.setInterpolator(new AccelerateInterpolator());
+ mEnteringAnimator.addUpdateListener(animation -> {
+ float progress = animation.getAnimatedFraction();
+ final int width = mStartTaskRect.width();
+ mEnteringMargin = width * progress;
+ // We don't animate to 0 or the surface would become invisible and lose focus.
+ final float alpha = progress >= 0.5f ? 0.01f
+ : mapRange(progress * 2, mCurrentAlpha, 0.01f);
+ mClosingRect.offset(mEnteringMargin, 0);
+ mEnteringRect.offset(mEnteringMargin - width, 0);
+
+ applyTransform(mClosingTarget.leash, mClosingRect, alpha);
+ applyTransform(mEnteringTarget.leash, mEnteringRect, mCurrentAlpha);
+ mTransaction.apply();
+ });
+ }
+
+ if (mEnteringAnimator.isRunning()) {
+ return true;
+ }
+
+ if (mEnteringWindowShow == show) {
+ return false;
+ }
+
+ mEnteringWindowShow = show;
+ if (show) {
+ mEnteringAnimator.start();
+ } else {
+ mEnteringAnimator.reverse();
+ }
+ return true;
+ }
+
+ private void onGestureCommitted() {
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ finishAnimation();
+ return;
+ }
+
+ // End the fade in animation.
+ if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
+ mEnteringAnimator.cancel();
+ }
+
+ // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
+ // coordinate of the gesture driven phase.
+ mEnteringRect.round(mEnteringStartRect);
+ mTransaction.hide(mClosingTarget.leash);
+
+ ValueAnimator valueAnimator =
+ ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION);
+ valueAnimator.setInterpolator(new DecelerateInterpolator());
+ valueAnimator.addUpdateListener(animation -> {
+ float progress = animation.getAnimatedFraction();
+ updatePostCommitEnteringAnimation(progress);
+ mTransaction.apply();
+ });
+
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishAnimation();
+ }
+ });
+ valueAnimator.start();
+ }
+
+ private void updatePostCommitEnteringAnimation(float progress) {
+ float left = mapRange(progress, mEnteringStartRect.left, mStartTaskRect.left);
+ float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top);
+ float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width());
+ float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());
+ float alpha = mapRange(progress, mCurrentAlpha, 1.0f);
+
+ mEnteringRect.set(left, top, left + width, top + height);
+ applyTransform(mEnteringTarget.leash, mEnteringRect, alpha);
+ }
+
+ private final class Callback extends IOnBackInvokedCallback.Default {
+ @Override
+ public void onBackStarted(BackMotionEvent backEvent) {
+ mProgressAnimator.onBackStarted(backEvent,
+ CrossActivityAnimation.this::onGestureProgress);
+ }
+
+ @Override
+ public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
+ mProgressAnimator.onBackProgressed(backEvent);
+ }
+
+ @Override
+ public void onBackCancelled() {
+ // End the fade in animation.
+ if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
+ mEnteringAnimator.cancel();
+ }
+ mProgressAnimator.onBackCancelled(CrossActivityAnimation.this::finishAnimation);
+ }
+
+ @Override
+ public void onBackInvoked() {
+ mProgressAnimator.reset();
+ onGestureCommitted();
+ }
+ }
+
+ private final class Runner extends IRemoteAnimationRunner.Default {
+ @Override
+ public void onAnimationStart(
+ int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to activity animation.");
+ for (RemoteAnimationTarget a : apps) {
+ if (a.mode == MODE_CLOSING) {
+ mClosingTarget = a;
+ }
+ if (a.mode == MODE_OPENING) {
+ mEnteringTarget = a;
+ }
+ }
+
+ startBackAnimation();
+ mFinishCallback = finishedCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ finishAnimation();
+ }
+ }
+}
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
new file mode 100644
index 000000000000..99a434aff799
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -0,0 +1,361 @@
+/*
+ * 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.back;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.BackEvent.EDGE_RIGHT;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.window.BackEvent;
+import android.window.BackMotionEvent;
+import android.window.BackProgressAnimator;
+import android.window.IOnBackInvokedCallback;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/**
+ * 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.
+ *
+ * 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 {
+ private static final int BACKGROUNDCOLOR = 0x43433A;
+
+ /**
+ * Minimum scale of the entering window.
+ */
+ private static final float ENTERING_MIN_WINDOW_SCALE = 0.85f;
+
+ /**
+ * Minimum scale of the closing window.
+ */
+ private static final float CLOSING_MIN_WINDOW_SCALE = 0.75f;
+
+ /**
+ * Minimum color scale of the closing window.
+ */
+ private static final float CLOSING_MIN_WINDOW_COLOR_SCALE = 0.1f;
+
+ /**
+ * The margin between the entering window and the closing window
+ */
+ private static final int WINDOW_MARGIN = 35;
+
+ /** Max window translation in the Y axis. */
+ private static final int WINDOW_MAX_DELTA_Y = 160;
+
+ private final Rect mStartTaskRect = new Rect();
+ private final float mCornerRadius;
+
+ // The closing window properties.
+ private final RectF mClosingCurrentRect = new RectF();
+
+ // The entering window properties.
+ private final Rect mEnteringStartRect = new Rect();
+ private final RectF mEnteringCurrentRect = new RectF();
+
+ private final PointF mInitialTouchPos = new PointF();
+ private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
+
+ private final Matrix mTransformMatrix = new Matrix();
+
+ private final float[] mTmpFloat9 = new float[9];
+ private final float[] mTmpTranslate = {0, 0, 0};
+
+ 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) {
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
+ mBackground = background;
+ }
+
+ private float getInterpolatedProgress(float backProgress) {
+ return 1 - (1 - backProgress) * (1 - backProgress) * (1 - backProgress);
+ }
+
+ private void startBackAnimation() {
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
+ return;
+ }
+
+ // Offset start rectangle to align task bounds.
+ mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds());
+ mStartTaskRect.offsetTo(0, 0);
+
+ // Draw background.
+ mBackground.ensureBackground(BACKGROUNDCOLOR, mTransaction);
+ }
+
+ private void updateGestureBackProgress(float progress, BackEvent event) {
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ return;
+ }
+
+ float touchX = event.getTouchX();
+ float touchY = event.getTouchY();
+ float dX = Math.abs(touchX - mInitialTouchPos.x);
+
+ // The 'follow width' is the width of the window if it completely matches
+ // the gesture displacement.
+ final int width = mStartTaskRect.width();
+ final int height = mStartTaskRect.height();
+
+ // The 'progress width' is the width of the window if it strictly linearly interpolates
+ // to minimum scale base on progress.
+ float enteringScale = mapRange(progress, 1, ENTERING_MIN_WINDOW_SCALE);
+ float closingScale = mapRange(progress, 1, CLOSING_MIN_WINDOW_SCALE);
+ float closingColorScale = mapRange(progress, 1, CLOSING_MIN_WINDOW_COLOR_SCALE);
+
+ // The final width is derived from interpolating between the follow with and progress width
+ // using gesture progress.
+ float enteringWidth = enteringScale * width;
+ float closingWidth = closingScale * width;
+ float enteringHeight = (float) height / width * enteringWidth;
+ float closingHeight = (float) height / width * closingWidth;
+
+ float deltaYRatio = (touchY - mInitialTouchPos.y) / height;
+ // Base the window movement in the Y axis on the touch movement in the Y axis.
+ float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * WINDOW_MAX_DELTA_Y;
+ // Move the window along the Y axis.
+ float closingTop = (height - closingHeight) * 0.5f + deltaY;
+ float enteringTop = (height - enteringHeight) * 0.5f + deltaY;
+ // Move the window along the X axis.
+ float right = width - (progress * WINDOW_MARGIN);
+ float left = right - closingWidth;
+
+ mClosingCurrentRect.set(left, closingTop, right, closingTop + closingHeight);
+ mEnteringCurrentRect.set(left - enteringWidth - WINDOW_MARGIN, enteringTop,
+ left - WINDOW_MARGIN, enteringTop + enteringHeight);
+
+ applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius);
+ applyColorTransform(mClosingTarget.leash, closingColorScale);
+ applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius);
+ mTransaction.apply();
+ }
+
+ private void updatePostCommitClosingAnimation(float progress) {
+ mTransaction.setLayer(mClosingTarget.leash, 0);
+ float alpha = mapRange(progress, 1, 0);
+ mTransaction.setAlpha(mClosingTarget.leash, alpha);
+ }
+
+ private void updatePostCommitEnteringAnimation(float progress) {
+ float left = mapRange(progress, mEnteringStartRect.left, mStartTaskRect.left);
+ float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top);
+ float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width());
+ float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());
+
+ mEnteringCurrentRect.set(left, top, left + width, top + height);
+ applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius);
+ }
+
+ /** Transform the target window to match the target rect. */
+ private void applyTransform(SurfaceControl leash, RectF targetRect, float cornerRadius) {
+ if (leash == null) {
+ return;
+ }
+
+ final float scale = targetRect.width() / mStartTaskRect.width();
+ mTransformMatrix.reset();
+ mTransformMatrix.setScale(scale, scale);
+ mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
+ mTransaction.setMatrix(leash, mTransformMatrix, mTmpFloat9)
+ .setWindowCrop(leash, mStartTaskRect)
+ .setCornerRadius(leash, cornerRadius);
+ }
+
+ private void applyColorTransform(SurfaceControl leash, float colorScale) {
+ if (leash == null) {
+ return;
+ }
+ computeScaleTransformMatrix(colorScale, mTmpFloat9);
+ 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();
+ mEnteringTarget = null;
+ }
+ if (mClosingTarget != null) {
+ mClosingTarget.leash.release();
+ mClosingTarget = null;
+ }
+
+ if (mBackground != null) {
+ mBackground.removeBackground(mTransaction);
+ }
+
+ mTransaction.apply();
+ mBackInProgress = false;
+ mTransformMatrix.reset();
+ mClosingCurrentRect.setEmpty();
+ mInitialTouchPos.set(0, 0);
+
+ if (mFinishCallback != null) {
+ try {
+ mFinishCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ mFinishCallback = null;
+ }
+ }
+
+ private void onGestureProgress(@NonNull BackEvent backEvent) {
+ if (!mBackInProgress) {
+ mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
+ mBackInProgress = true;
+ }
+ mProgress = backEvent.getProgress();
+ mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ updateGestureBackProgress(getInterpolatedProgress(mProgress), backEvent);
+ }
+
+ private void onGestureCommitted() {
+ if (mEnteringTarget == null || mClosingTarget == null) {
+ finishAnimation();
+ return;
+ }
+
+ // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
+ // coordinate of the gesture driven phase.
+ mEnteringCurrentRect.round(mEnteringStartRect);
+
+ ValueAnimator valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(300);
+ valueAnimator.setInterpolator(mInterpolator);
+ valueAnimator.addUpdateListener(animation -> {
+ float progress = animation.getAnimatedFraction();
+ updatePostCommitEnteringAnimation(progress);
+ updatePostCommitClosingAnimation(progress);
+ mTransaction.apply();
+ });
+
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishAnimation();
+ }
+ });
+ valueAnimator.start();
+ }
+
+ private static float mapRange(float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
+ private final class Callback extends IOnBackInvokedCallback.Default {
+ @Override
+ public void onBackStarted(BackMotionEvent backEvent) {
+ mProgressAnimator.onBackStarted(backEvent,
+ CrossTaskBackAnimation.this::onGestureProgress);
+ }
+
+ @Override
+ public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
+ mProgressAnimator.onBackProgressed(backEvent);
+ }
+
+ @Override
+ public void onBackCancelled() {
+ mProgressAnimator.onBackCancelled(CrossTaskBackAnimation.this::finishAnimation);
+ }
+
+ @Override
+ public void onBackInvoked() {
+ mProgressAnimator.reset();
+ onGestureCommitted();
+ }
+ };
+
+ private final class Runner extends IRemoteAnimationRunner.Default {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to task animation.");
+ for (RemoteAnimationTarget a : apps) {
+ if (a.mode == MODE_CLOSING) {
+ mClosingTarget = a;
+ }
+ if (a.mode == MODE_OPENING) {
+ mEnteringTarget = a;
+ }
+ }
+
+ startBackAnimation();
+ mFinishCallback = finishedCallback;
+ }
+ };
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
index 6311f879fd45..2b2a0e397792 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
@@ -17,29 +17,21 @@
package com.android.wm.shell.back;
import android.window.IOnBackInvokedCallback;
+import android.view.IRemoteAnimationRunner;
/**
* Interface for Launcher process to register back invocation callbacks.
*/
interface IBackAnimation {
-
/**
- * Sets a {@link IOnBackInvokedCallback} to be invoked when
+ * Sets a {@link IOnBackInvokedCallback} and a {@link IRemoteAnimationRunner} to be invoked when
* back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}.
*/
- void setBackToLauncherCallback(in IOnBackInvokedCallback callback);
+ void setBackToLauncherCallback(in IOnBackInvokedCallback callback,
+ in IRemoteAnimationRunner runner);
/**
* Clears the previously registered {@link IOnBackInvokedCallback}.
*/
void clearBackToLauncherCallback();
-
- /**
- * Notifies Shell that the back to launcher animation has fully finished
- * (including the transition animation that runs after the finger is lifted).
- *
- * At this point the top window leash (if one was created) should be ready to be released.
- * //TODO: Remove once we play the transition animation through shell transitions.
- */
- void onBackToLauncherAnimationFinished();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index ccfac65d6342..695ef4e66302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.back;
import android.os.SystemProperties;
import android.view.RemoteAnimationTarget;
import android.window.BackEvent;
+import android.window.BackMotionEvent;
/**
* Helper class to record the touch location for gesture and generate back events.
@@ -82,11 +83,11 @@ class TouchTracker {
mSwipeEdge = BackEvent.EDGE_LEFT;
}
- BackEvent createStartEvent(RemoteAnimationTarget target) {
- return new BackEvent(mInitTouchX, mInitTouchY, 0, mSwipeEdge, target);
+ BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+ return new BackMotionEvent(mInitTouchX, mInitTouchY, 0, mSwipeEdge, target);
}
- BackEvent createProgressEvent() {
+ BackMotionEvent createProgressEvent() {
float progressThreshold = PROGRESS_THRESHOLD >= 0
? PROGRESS_THRESHOLD : mProgressThreshold;
progressThreshold = progressThreshold == 0 ? 1 : progressThreshold;
@@ -109,8 +110,8 @@ class TouchTracker {
return createProgressEvent(progress);
}
- BackEvent createProgressEvent(float progress) {
- return new BackEvent(mLatestTouchX, mLatestTouchY, progress, mSwipeEdge, null);
+ BackMotionEvent createProgressEvent(float progress) {
+ return new BackMotionEvent(mLatestTouchX, mLatestTouchY, progress, mSwipeEdge, null);
}
public void setProgressThreshold(float progressThreshold) {
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 dd8afff0df2c..9674b69baa00 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
@@ -685,10 +685,18 @@ public class BubbleController implements ConfigurationChangeListener {
return;
}
+ mAddedToWindowManager = false;
+ // Put on background for this binder call, was causing jank
+ mBackgroundExecutor.execute(() -> {
+ try {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ } catch (IllegalArgumentException e) {
+ // Not sure if this happens in production, but was happening in tests
+ // (b/253647225)
+ e.printStackTrace();
+ }
+ });
try {
- mAddedToWindowManager = false;
- // Put on background for this binder call, was causing jank
- mBackgroundExecutor.execute(() -> mContext.unregisterReceiver(mBroadcastReceiver));
if (mStackView != null) {
mWindowManager.removeView(mStackView);
mBubbleData.getOverflow().cleanUpExpandedState();
@@ -706,7 +714,7 @@ public class BubbleController implements ConfigurationChangeListener {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
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 5ea370b65407..8121b206c93a 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
@@ -53,13 +53,13 @@ import android.util.IntProperty;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.window.ScreenCapture;
import androidx.annotation.Nullable;
@@ -516,7 +516,7 @@ public class BubbleExpandedView extends LinearLayout {
/** Return a GraphicBuffer with the contents of the task view surface. */
@Nullable
- SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
+ ScreenCapture.ScreenshotHardwareBuffer snapshotActivitySurface() {
if (mIsOverflow) {
// For now, just snapshot the view and return it as a hw buffer so that the animation
// code for both the tasks and overflow can be the same
@@ -525,7 +525,7 @@ public class BubbleExpandedView extends LinearLayout {
p.beginRecording(mOverflowView.getWidth(), mOverflowView.getHeight()));
p.endRecording();
Bitmap snapshot = Bitmap.createBitmap(p);
- return new SurfaceControl.ScreenshotHardwareBuffer(
+ return new ScreenCapture.ScreenshotHardwareBuffer(
snapshot.getHardwareBuffer(),
snapshot.getColorSpace(),
false /* containsSecureLayers */,
@@ -534,7 +534,7 @@ public class BubbleExpandedView extends LinearLayout {
if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
return null;
}
- return SurfaceControl.captureLayers(
+ return ScreenCapture.captureLayers(
mTaskView.getSurfaceControl(),
new Rect(0, 0, mTaskView.getWidth(), mTaskView.getHeight()),
1 /* scale */);
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 f2afefe243bc..abe42eec7061 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
@@ -51,7 +51,6 @@ import android.util.Log;
import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
@@ -64,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import android.window.ScreenCapture;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -238,7 +238,7 @@ public class BubbleStackView extends FrameLayout
* Buffer containing a screenshot of the animating-out bubble. This is drawn into the
* SurfaceView during animations.
*/
- private SurfaceControl.ScreenshotHardwareBuffer mAnimatingOutBubbleBuffer;
+ private ScreenCapture.ScreenshotHardwareBuffer mAnimatingOutBubbleBuffer;
private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
@@ -920,7 +920,6 @@ public class BubbleStackView extends FrameLayout
addView(mAnimatingOutSurfaceContainer);
mAnimatingOutSurfaceView = new SurfaceView(getContext());
- mAnimatingOutSurfaceView.setUseAlpha();
mAnimatingOutSurfaceView.setZOrderOnTop(true);
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 7f7af935ff2b..d0aef2023048 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -16,17 +16,23 @@
package com.android.wm.shell.common;
+import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL;
+import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END;
+import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START;
+import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
+import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.ComponentName;
-import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
@@ -34,19 +40,21 @@ import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsVisibilities;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowInsets;
+import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodManagerGlobal;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.view.IInputMethodManager;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -114,7 +122,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
- pd.startAnimation(true, false /* forceRestart */);
+ pd.startAnimation(true, false /* forceRestart */, null /* statsToken */);
}
}
@@ -209,7 +217,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
- final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
+ @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
InsetsSourceControl mImeSourceControl = null;
int mAnimationDirection = DIRECTION_NONE;
ValueAnimator mAnimation = null;
@@ -246,7 +254,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mInsetsState.set(insetsState, true /* copySources */);
if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
- startAnimation(mImeShowing, true /* forceRestart */);
+ startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
}
}
@@ -261,7 +269,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (activeControl == null) {
continue;
}
- if (activeControl.getType() == InsetsState.ITYPE_IME) {
+ if (activeControl.getType() == WindowInsets.Type.ime()) {
imeSourceControl = activeControl;
}
}
@@ -280,7 +288,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final boolean positionChanged =
!imeSourceControl.getSurfacePosition().equals(lastSurfacePosition);
if (positionChanged) {
- startAnimation(mImeShowing, true /* forceRestart */);
+ startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
}
} else {
if (!haveSameLeash(mImeSourceControl, imeSourceControl)) {
@@ -315,26 +323,27 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
@Override
- public void showInsets(int types, boolean fromIme) {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- startAnimation(true /* show */, false /* forceRestart */);
+ startAnimation(true /* show */, false /* forceRestart */, statsToken);
}
@Override
- public void hideInsets(int types, boolean fromIme) {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- startAnimation(false /* show */, false /* forceRestart */);
+ startAnimation(false /* show */, false /* forceRestart */, statsToken);
}
@Override
- public void topFocusedWindowChanged(ComponentName component,
- InsetsVisibilities requestedVisibilities) {
+ public void topFocusedWindowChanged(ComponentName component, int requestedVisibleTypes) {
// Do nothing
}
@@ -343,10 +352,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*/
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
- mRequestedVisibilities.setVisibility(InsetsState.ITYPE_IME, visible);
+ mRequestedVisibleTypes = visible
+ ? mRequestedVisibleTypes | WindowInsets.Type.ime()
+ : mRequestedVisibleTypes & ~WindowInsets.Type.ime();
try {
- mWmService.updateDisplayWindowRequestedVisibilities(mDisplayId,
- mRequestedVisibilities);
+ mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId,
+ mRequestedVisibleTypes);
} catch (RemoteException e) {
}
}
@@ -369,9 +380,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
.navBarFrameHeight();
}
- private void startAnimation(final boolean show, final boolean forceRestart) {
+ private void startAnimation(final boolean show, final boolean forceRestart,
+ @Nullable ImeTracker.Token statsToken) {
final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
if (imeSource == null || mImeSourceControl == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
final Rect newFrame = imeSource.getFrame();
@@ -392,8 +405,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
+ (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
: (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
}
- if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show)
+ if ((!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show))
|| (mAnimationDirection == DIRECTION_HIDE && !show)) {
+ ImeTracker.get().onCancelled(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
return;
}
boolean seek = false;
@@ -437,8 +451,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mTransactionPool.release(t);
});
mAnimation.setInterpolator(INTERPOLATOR);
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
+ @Nullable
+ private final ImeTracker.Token mStatsToken = statsToken;
@Override
public void onAnimationStart(Animator animation) {
@@ -457,8 +474,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
: 1.f;
t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
+ ImeTracker.get().onProgress(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.show(mImeSourceControl.getLeash());
}
+ if (DEBUG_IME_VISIBILITY) {
+ EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
+ statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mDisplayId, mAnimationDirection, alpha, startY , endY,
+ Objects.toString(mImeSourceControl.getLeash()),
+ Objects.toString(mImeSourceControl.getInsetsHint()),
+ Objects.toString(mImeSourceControl.getSurfacePosition()),
+ Objects.toString(mImeFrame));
+ }
t.apply();
mTransactionPool.release(t);
}
@@ -466,6 +494,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
+ if (DEBUG_IME_VISIBILITY) {
+ EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
+ statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId,
+ Objects.toString(mImeSourceControl.getInsetsHint()));
+ }
}
@Override
@@ -478,8 +511,25 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
+ ImeTracker.get().onProgress(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.hide(mImeSourceControl.getLeash());
removeImeSurface();
+ ImeTracker.get().onHidden(mStatsToken);
+ } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
+ ImeTracker.get().onShown(mStatsToken);
+ } else if (mCancelled) {
+ ImeTracker.get().onCancelled(mStatsToken,
+ ImeTracker.PHASE_WM_ANIMATION_RUNNING);
+ }
+ if (DEBUG_IME_VISIBILITY) {
+ EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
+ statsToken != null ? statsToken.getTag() : TOKEN_NONE,
+ mDisplayId, mAnimationDirection, endY,
+ Objects.toString(mImeSourceControl.getLeash()),
+ Objects.toString(mImeSourceControl.getInsetsHint()),
+ Objects.toString(mImeSourceControl.getSurfacePosition()),
+ Objects.toString(mImeFrame));
}
t.apply();
mTransactionPool.release(t);
@@ -515,16 +565,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
void removeImeSurface() {
- final IInputMethodManager imms = getImms();
- if (imms != null) {
- try {
- // Remove the IME surface to make the insets invisible for
- // non-client controlled insets.
- imms.removeImeSurface();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to remove IME surface.", e);
- }
- }
+ // Remove the IME surface to make the insets invisible for
+ // non-client controlled insets.
+ InputMethodManagerGlobal.removeImeSurface(
+ e -> Slog.e(TAG, "Failed to remove IME surface.", e));
}
/**
@@ -598,11 +642,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- public IInputMethodManager getImms() {
- return IInputMethodManager.Stub.asInterface(
- ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
- }
-
private static boolean haveSameLeash(InsetsSourceControl a, InsetsSourceControl b) {
if (a == b) {
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index 90a01f8c5295..8759301f695b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
import android.util.Slog;
@@ -24,7 +25,8 @@ import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsVisibilities;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
import androidx.annotation.BinderThread;
@@ -156,34 +158,40 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
}
}
- private void showInsets(int types, boolean fromIme) {
+ private void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
- listener.showInsets(types, fromIme);
+ listener.showInsets(types, fromIme, statsToken);
}
}
- private void hideInsets(int types, boolean fromIme) {
+ private void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
+ ImeTracker.get().onFailed(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
+ ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
- listener.hideInsets(types, fromIme);
+ listener.hideInsets(types, fromIme, statsToken);
}
}
private void topFocusedWindowChanged(ComponentName component,
- InsetsVisibilities requestedVisibilities) {
+ @InsetsType int requestedVisibleTypes) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
- listener.topFocusedWindowChanged(component, requestedVisibilities);
+ listener.topFocusedWindowChanged(component, requestedVisibleTypes);
}
}
@@ -192,9 +200,9 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
extends IDisplayWindowInsetsController.Stub {
@Override
public void topFocusedWindowChanged(ComponentName component,
- InsetsVisibilities requestedVisibilities) throws RemoteException {
+ @InsetsType int requestedVisibleTypes) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.topFocusedWindowChanged(component, requestedVisibilities);
+ PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes);
});
}
@@ -214,16 +222,18 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
}
@Override
- public void showInsets(int types, boolean fromIme) throws RemoteException {
+ public void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.showInsets(types, fromIme);
+ PerDisplay.this.showInsets(types, fromIme, statsToken);
});
}
@Override
- public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ public void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.hideInsets(types, fromIme);
+ PerDisplay.this.hideInsets(types, fromIme, statsToken);
});
}
}
@@ -239,11 +249,13 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+ *
* @param component The application component that is open in the top focussed window.
- * @param requestedVisibilities The insets visibilities requested by the focussed window.
+ * @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused
+ * window.
*/
default void topFocusedWindowChanged(ComponentName component,
- InsetsVisibilities requestedVisibilities) {}
+ @InsetsType int requestedVisibleTypes) {}
/**
* Called when the window insets configuration has changed.
@@ -259,17 +271,23 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
/**
* Called when a set of insets source window should be shown by policy.
*
- * @param types internal insets types (WindowInsets.Type.InsetsType) to show
+ * @param types {@link InsetsType} to show
* @param fromIme true if this request originated from IME (InputMethodService).
+ * @param statsToken the token tracking the current IME show request
+ * or {@code null} otherwise.
*/
- default void showInsets(int types, boolean fromIme) {}
+ default void showInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {}
/**
* Called when a set of insets source window should be hidden by policy.
*
- * @param types internal insets types (WindowInsets.Type.InsetsType) to hide
+ * @param types {@link InsetsType} to hide
* @param fromIme true if this request originated from IME (InputMethodService).
+ * @param statsToken the token tracking the current IME hide request
+ * or {@code null} otherwise.
*/
- default void hideInsets(int types, boolean fromIme) {}
+ default void hideInsets(@InsetsType int types, boolean fromIme,
+ @Nullable ImeTracker.Token statsToken) {}
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index 2a1bf0ee42ba..fad3dee1f927 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.window.ScreenCapture;
import java.util.function.Consumer;
@@ -35,9 +36,9 @@ public class ScreenshotUtils {
* @param consumer Consumer for the captured buffer
*/
public static void captureLayer(SurfaceControl sc, Rect crop,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
- consumer.accept(SurfaceControl.captureLayers(
- new SurfaceControl.LayerCaptureArgs.Builder(sc)
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> consumer) {
+ consumer.accept(ScreenCapture.captureLayers(
+ new ScreenCapture.LayerCaptureArgs.Builder(sc)
.setSourceCrop(crop)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
@@ -45,7 +46,7 @@ public class ScreenshotUtils {
}
private static class BufferConsumer implements
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> {
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> {
SurfaceControl mScreenshot = null;
SurfaceControl.Transaction mTransaction;
SurfaceControl mSurfaceControl;
@@ -61,7 +62,7 @@ public class ScreenshotUtils {
}
@Override
- public void accept(SurfaceControl.ScreenshotHardwareBuffer buffer) {
+ public void accept(ScreenCapture.ScreenshotHardwareBuffer buffer) {
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index e270edb800bd..94aeb2beb1e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Region;
@@ -46,6 +47,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -344,17 +346,17 @@ public class SystemWindows {
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration newMergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
- int resizeMode) {}
+ boolean dragResizing) {}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {}
@Override
- public void showInsets(int types, boolean fromIme) {}
+ public void showInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {}
@Override
- public void hideInsets(int types, boolean fromIme) {}
+ public void hideInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {}
@Override
public void moved(int newX, int newY) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
index a09aab666a31..8ba785a1f03a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip.tv;
+package com.android.wm.shell.common;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -28,36 +30,34 @@ import android.widget.RelativeLayout;
import com.android.wm.shell.R;
/**
- * A View that represents Pip Menu action button, such as "Fullscreen" and "Close" as well custom
- * (provided by the application in Pip) and media buttons.
+ * A common action button for TV window menu layouts.
*/
-public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
+public class TvWindowMenuActionButton extends RelativeLayout {
private final ImageView mIconImageView;
private final View mButtonBackgroundView;
- private final View mButtonView;
- private OnClickListener mOnClickListener;
- public TvPipMenuActionButton(Context context) {
+ private Icon mCurrentIcon;
+
+ public TvWindowMenuActionButton(Context context) {
this(context, null, 0, 0);
}
- public TvPipMenuActionButton(Context context, AttributeSet attrs) {
+ public TvWindowMenuActionButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
- public TvPipMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ public TvWindowMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public TvPipMenuActionButton(
+ public TvWindowMenuActionButton(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.tv_pip_menu_action_button, this);
+ inflater.inflate(R.layout.tv_window_menu_action_button, this);
mIconImageView = findViewById(R.id.icon);
- mButtonView = findViewById(R.id.button);
mButtonBackgroundView = findViewById(R.id.background);
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
@@ -72,23 +72,6 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
typedArray.recycle();
}
- @Override
- public void setOnClickListener(OnClickListener listener) {
- // We do not want to set an OnClickListener to the TvPipMenuActionButton itself, but only to
- // the ImageView. So let's "cash" the listener we've been passed here and set a "proxy"
- // listener to the ImageView.
- mOnClickListener = listener;
- mButtonView.setOnClickListener(listener != null ? this : null);
- }
-
- @Override
- public void onClick(View v) {
- if (mOnClickListener != null) {
- // Pass the correct view - this.
- mOnClickListener.onClick(this);
- }
- }
-
/**
* Sets the drawable for the button with the given drawable.
*/
@@ -105,11 +88,24 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
}
}
+ public void setImageIconAsync(Icon icon, Handler handler) {
+ mCurrentIcon = icon;
+ // Remove old image while waiting for the new one to load.
+ mIconImageView.setImageDrawable(null);
+ icon.loadDrawableAsync(mContext, d -> {
+ // The image hasn't been set any other way and the drawable belongs to the most
+ // recently set Icon.
+ if (mIconImageView.getDrawable() == null && mCurrentIcon == icon) {
+ mIconImageView.setImageDrawable(d);
+ }
+ }, handler);
+ }
+
/**
* Sets the text for description the with the given string.
*/
public void setTextAndDescription(CharSequence text) {
- mButtonView.setContentDescription(text);
+ setContentDescription(text);
}
/**
@@ -119,32 +115,29 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
setTextAndDescription(getContext().getString(resId));
}
- @Override
- public void setEnabled(boolean enabled) {
- mButtonView.setEnabled(enabled);
- }
-
- @Override
- public boolean isEnabled() {
- return mButtonView.isEnabled();
- }
-
- void setIsCustomCloseAction(boolean isCustomCloseAction) {
+ /**
+ * Marks this button as a custom close action button.
+ * This changes the style of the action button to highlight that this action finishes the
+ * Picture-in-Picture activity.
+ *
+ * @param isCustomCloseAction sets or unsets this button as a custom close action button.
+ */
+ public void setIsCustomCloseAction(boolean isCustomCloseAction) {
mIconImageView.setImageTintList(
getResources().getColorStateList(
- isCustomCloseAction ? R.color.tv_pip_menu_close_icon
- : R.color.tv_pip_menu_icon));
+ isCustomCloseAction ? R.color.tv_window_menu_close_icon
+ : R.color.tv_window_menu_icon));
mButtonBackgroundView.setBackgroundTintList(getResources()
- .getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg
- : R.color.tv_pip_menu_icon_bg));
+ .getColorStateList(isCustomCloseAction ? R.color.tv_window_menu_close_icon_bg
+ : R.color.tv_window_menu_icon_bg));
}
@Override
public String toString() {
- if (mButtonView.getContentDescription() == null) {
- return TvPipMenuActionButton.class.getSimpleName();
+ if (getContentDescription() == null) {
+ return TvWindowMenuActionButton.class.getSimpleName();
}
- return mButtonView.getContentDescription().toString();
+ return getContentDescription().toString();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 060ac56cae96..5397552cbe8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -126,6 +126,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
lp.token = new Binder();
lp.setTitle(mWindowName);
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ lp.accessibilityTitle = mContext.getResources().getString(R.string.accessibility_divider);
mViewHost.setView(mDividerView, lp);
mDividerView.setup(splitLayout, this, mViewHost, insetsState);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 8022e9b1cd81..b144d22fc3ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -81,6 +81,7 @@ public abstract class TvPipModule {
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper windowManagerShellWrapper,
+ @ShellMainThread Handler mainHandler, // needed for registerReceiverForAllUsers()
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(
TvPipController.create(
@@ -100,6 +101,7 @@ public abstract class TvPipModule {
pipParamsChangedForwarder,
displayController,
windowManagerShellWrapper,
+ mainHandler,
mainExecutor));
}
@@ -157,22 +159,17 @@ public abstract class TvPipModule {
Context context,
TvPipBoundsState tvPipBoundsState,
SystemWindows systemWindows,
- PipMediaController pipMediaController,
@ShellMainThread Handler mainHandler) {
- return new TvPipMenuController(context, tvPipBoundsState, systemWindows, pipMediaController,
- mainHandler);
+ return new TvPipMenuController(context, tvPipBoundsState, systemWindows, mainHandler);
}
- // Handler needed for registerReceiverForAllUsers()
@WMSingleton
@Provides
static TvPipNotificationController provideTvPipNotificationController(Context context,
PipMediaController pipMediaController,
- PipParamsChangedForwarder pipParamsChangedForwarder,
- TvPipBoundsState tvPipBoundsState,
- @ShellMainThread Handler mainHandler) {
+ PipParamsChangedForwarder pipParamsChangedForwarder) {
return new TvPipNotificationController(context, pipMediaController,
- pipParamsChangedForwarder, tvPipBoundsState, mainHandler);
+ pipParamsChangedForwarder);
}
@WMSingleton
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 15bfeb297b41..e9957fd4f4f1 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
@@ -16,16 +16,32 @@
package com.android.wm.shell.dagger;
-import android.view.IWindowManager;
+import android.content.Context;
+import android.os.Handler;
+import com.android.launcher3.icons.IconProvider;
+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.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
+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.transition.Transitions;
+
+import java.util.Optional;
import dagger.Module;
import dagger.Provides;
@@ -50,5 +66,33 @@ public class TvWMShellModule {
@DynamicOverride
static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
return new TvStartingWindowTypeAlgorithm();
- };
+ }
+
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static SplitScreenController provideSplitScreenController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ DragAndDropController dragAndDropController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
+ @ShellMainThread ShellExecutor mainExecutor,
+ Handler mainHandler,
+ SystemWindows systemWindows) {
+ return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
+ shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
+ displayImeController, displayInsetsController, dragAndDropController, transitions,
+ transactionPool, iconProvider, recentTasks, mainExecutor, mainHandler,
+ systemWindows);
+ }
}
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 09f5cf1d31e4..7055aca0ae8a 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.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
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.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -94,13 +95,13 @@ 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
@@ -256,21 +257,30 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
+ static BackAnimationBackground provideBackAnimationBackground(
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ return new BackAnimationBackground(rootTaskDisplayAreaOrganizer);
+ }
+
+ @WMSingleton
+ @Provides
static Optional<BackAnimationController> provideBackAnimationController(
Context context,
ShellInit shellInit,
ShellController shellController,
@ShellMainThread ShellExecutor shellExecutor,
- @ShellBackgroundThread Handler backgroundHandler
+ @ShellBackgroundThread Handler backgroundHandler,
+ BackAnimationBackground backAnimationBackground
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
new BackAnimationController(shellInit, shellController, shellExecutor,
- backgroundHandler, context));
+ backgroundHandler, context, backAnimationBackground));
}
return Optional.empty();
}
+
//
// Bubbles (optional feature)
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 16f1d1c2944c..000624499f79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -26,11 +26,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.RemoteAction;
+import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.wm.shell.R;
+
import java.util.List;
/**
@@ -97,16 +100,20 @@ public interface PipMenuController {
/**
* Returns a default LayoutParams for the PIP Menu.
+ * @param context the context.
* @param width the PIP stack width.
* @param height the PIP stack height.
*/
- default WindowManager.LayoutParams getPipMenuLayoutParams(String title, int width, int height) {
+ default WindowManager.LayoutParams getPipMenuLayoutParams(Context context, String title,
+ int width, int height) {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
TYPE_APPLICATION_OVERLAY,
FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY;
lp.setTitle(title);
+ lp.accessibilityTitle = context.getResources().getString(
+ R.string.pip_menu_accessibility_title);
return lp;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 281ea530e9e1..a0a8f9fb2cde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -186,7 +186,7 @@ public class PhonePipMenuController implements PipMenuController {
mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
mSplitScreenController, mPipUiEventLogger);
mSystemWindows.addView(mPipMenuView,
- getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
+ getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
setShellRootAccessibilityWindow();
@@ -210,7 +210,7 @@ public class PhonePipMenuController implements PipMenuController {
@Override
public void updateMenuBounds(Rect destinationBounds) {
mSystemWindows.updateViewLayout(mPipMenuView,
- getPipMenuLayoutParams(MENU_WINDOW_TITLE, destinationBounds.width(),
+ getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, destinationBounds.width(),
destinationBounds.height()));
updateMenuLayout(destinationBounds);
}
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 7365b9525919..4a06d84ce90d 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
@@ -24,11 +24,13 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.window.ScreenCapture;
import androidx.annotation.BinderThread;
@@ -362,6 +364,15 @@ public class PipAccessibilityInteractionConnection {
}
@Override
+ public void takeScreenshotOfWindow(int interactionId,
+ ScreenCapture.ScreenCaptureListener listener,
+ IAccessibilityInteractionConnectionCallback callback) throws RemoteException {
+ // AbstractAccessibilityServiceConnection uses the standard
+ // IAccessibilityInteractionConnection for takeScreenshotOfWindow for Pip windows,
+ // so do nothing here.
+ }
+
+ @Override
public void clearAccessibilityFocus() throws RemoteException {
// Do nothing
}
@@ -370,5 +381,8 @@ public class PipAccessibilityInteractionConnection {
public void notifyOutsideTouch() throws RemoteException {
// Do nothing
}
- }
+
+ @Override
+ public void attachAccessibilityOverlayToWindow(SurfaceControl sc) {}
}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
index acc0caf95e35..d7d335b856be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
@@ -43,16 +43,16 @@ public class PipDoubleTapHelper {
* <p>MAX - maximum allowed screen size</p>
*/
@IntDef(value = {
- SIZE_SPEC_CUSTOM,
SIZE_SPEC_DEFAULT,
- SIZE_SPEC_MAX
+ SIZE_SPEC_MAX,
+ SIZE_SPEC_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@interface PipSizeSpec {}
- static final int SIZE_SPEC_CUSTOM = 2;
static final int SIZE_SPEC_DEFAULT = 0;
static final int SIZE_SPEC_MAX = 1;
+ static final int SIZE_SPEC_CUSTOM = 2;
/**
* Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
index 85441af9a870..5aa3c4e2abef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module TV pip owner
galinap@google.com
+bronger@google.com
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
new file mode 100644
index 000000000000..222307fba8c2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
@@ -0,0 +1,87 @@
+/*
+ * 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.pip.tv;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.wm.shell.common.TvWindowMenuActionButton;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+abstract class TvPipAction {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ACTION_"}, value = {
+ ACTION_FULLSCREEN,
+ ACTION_CLOSE,
+ ACTION_MOVE,
+ ACTION_EXPAND_COLLAPSE,
+ ACTION_CUSTOM,
+ ACTION_CUSTOM_CLOSE
+ })
+ public @interface ActionType {
+ }
+
+ public static final int ACTION_FULLSCREEN = 0;
+ public static final int ACTION_CLOSE = 1;
+ public static final int ACTION_MOVE = 2;
+ public static final int ACTION_EXPAND_COLLAPSE = 3;
+ public static final int ACTION_CUSTOM = 4;
+ public static final int ACTION_CUSTOM_CLOSE = 5;
+
+ @ActionType
+ private final int mActionType;
+
+ @NonNull
+ private final SystemActionsHandler mSystemActionsHandler;
+
+ TvPipAction(@ActionType int actionType, @NonNull SystemActionsHandler systemActionsHandler) {
+ Objects.requireNonNull(systemActionsHandler);
+ mActionType = actionType;
+ mSystemActionsHandler = systemActionsHandler;
+ }
+
+ boolean isCloseAction() {
+ return mActionType == ACTION_CLOSE || mActionType == ACTION_CUSTOM_CLOSE;
+ }
+
+ @ActionType
+ int getActionType() {
+ return mActionType;
+ }
+
+ abstract void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler);
+
+ abstract PendingIntent getPendingIntent();
+
+ void executeAction() {
+ mSystemActionsHandler.executeAction(mActionType);
+ }
+
+ abstract Notification.Action toNotificationAction(Context context);
+
+ interface SystemActionsHandler {
+ void executeAction(@TvPipAction.ActionType int actionType);
+ }
+}
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
new file mode 100644
index 000000000000..fa62a73ca9b4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -0,0 +1,245 @@
+/*
+ * 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.pip.tv;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CLOSE;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM_CLOSE;
+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 com.android.wm.shell.pip.tv.TvPipController.ACTION_CLOSE_PIP;
+import static com.android.wm.shell.pip.tv.TvPipController.ACTION_MOVE_PIP;
+import static com.android.wm.shell.pip.tv.TvPipController.ACTION_TOGGLE_EXPANDED_PIP;
+import static com.android.wm.shell.pip.tv.TvPipController.ACTION_TO_FULLSCREEN;
+
+import android.annotation.NonNull;
+import android.app.RemoteAction;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Creates the system TvPipActions (fullscreen, close, move, expand/collapse), and handles all the
+ * changes to the actions, including the custom app actions and media actions. Other components can
+ * listen to those changes.
+ */
+public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler {
+ private static final String TAG = TvPipActionsProvider.class.getSimpleName();
+
+ private static final int CLOSE_ACTION_INDEX = 1;
+ private static final int FIRST_CUSTOM_ACTION_INDEX = 2;
+
+ private final List<Listener> mListeners = new ArrayList<>();
+ private final TvPipAction.SystemActionsHandler mSystemActionsHandler;
+
+ private final List<TvPipAction> mActionsList;
+ private final TvPipSystemAction mDefaultCloseAction;
+ private final TvPipSystemAction mExpandCollapseAction;
+
+ private final List<RemoteAction> mMediaActions = new ArrayList<>();
+ private final List<RemoteAction> mAppActions = new ArrayList<>();
+
+ public TvPipActionsProvider(Context context, PipMediaController pipMediaController,
+ TvPipAction.SystemActionsHandler systemActionsHandler) {
+ mSystemActionsHandler = systemActionsHandler;
+
+ mActionsList = new ArrayList<>();
+ mActionsList.add(new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen,
+ R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context,
+ 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));
+
+ mExpandCollapseAction = new TvPipSystemAction(ACTION_EXPAND_COLLAPSE, R.string.pip_collapse,
+ R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context,
+ mSystemActionsHandler);
+ mActionsList.add(mExpandCollapseAction);
+
+ pipMediaController.addActionListener(this::onMediaActionsChanged);
+ }
+
+ @Override
+ public void executeAction(@TvPipAction.ActionType int actionType) {
+ if (mSystemActionsHandler != null) {
+ mSystemActionsHandler.executeAction(actionType);
+ }
+ }
+
+ private void notifyActionsChanged(int added, int changed, int startIndex) {
+ for (Listener listener : mListeners) {
+ listener.onActionsChanged(added, changed, startIndex);
+ }
+ }
+
+ @VisibleForTesting(visibility = PACKAGE)
+ public void setAppActions(@NonNull List<RemoteAction> appActions, RemoteAction closeAction) {
+ // Update close action.
+ mActionsList.set(CLOSE_ACTION_INDEX,
+ closeAction == null ? mDefaultCloseAction
+ : new TvPipCustomAction(ACTION_CUSTOM_CLOSE, closeAction,
+ mSystemActionsHandler));
+ notifyActionsChanged(/* added= */ 0, /* updated= */ 1, CLOSE_ACTION_INDEX);
+
+ // Replace custom actions with new ones.
+ mAppActions.clear();
+ for (RemoteAction action : appActions) {
+ if (action != null && !PipUtils.remoteActionsMatch(action, closeAction)) {
+ // Only show actions that aren't duplicates of the custom close action.
+ mAppActions.add(action);
+ }
+ }
+
+ updateCustomActions(mAppActions);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void onMediaActionsChanged(List<RemoteAction> actions) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onMediaActionsChanged()", TAG);
+
+ mMediaActions.clear();
+ // Don't show disabled actions.
+ for (RemoteAction remoteAction : actions) {
+ if (remoteAction.isEnabled()) {
+ mMediaActions.add(remoteAction);
+ }
+ }
+
+ updateCustomActions(mMediaActions);
+ }
+
+ private void updateCustomActions(@NonNull List<RemoteAction> customActions) {
+ List<RemoteAction> newCustomActions = customActions;
+ if (newCustomActions == mMediaActions && !mAppActions.isEmpty()) {
+ // Don't show the media actions while there are app actions.
+ return;
+ } else if (newCustomActions == mAppActions && mAppActions.isEmpty()) {
+ // If all the app actions were removed, show the media actions.
+ newCustomActions = mMediaActions;
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: replaceCustomActions, count: %d", TAG, newCustomActions.size());
+ int oldCustomActionsCount = 0;
+ for (TvPipAction action : mActionsList) {
+ if (action.getActionType() == ACTION_CUSTOM) {
+ oldCustomActionsCount++;
+ }
+ }
+ mActionsList.removeIf(tvPipAction -> tvPipAction.getActionType() == ACTION_CUSTOM);
+
+ List<TvPipAction> actions = new ArrayList<>();
+ for (RemoteAction action : newCustomActions) {
+ actions.add(new TvPipCustomAction(ACTION_CUSTOM, action, mSystemActionsHandler));
+ }
+ mActionsList.addAll(FIRST_CUSTOM_ACTION_INDEX, actions);
+
+ int added = newCustomActions.size() - oldCustomActionsCount;
+ int changed = Math.min(newCustomActions.size(), oldCustomActionsCount);
+ notifyActionsChanged(added, changed, FIRST_CUSTOM_ACTION_INDEX);
+ }
+
+ @VisibleForTesting(visibility = PACKAGE)
+ public void updateExpansionEnabled(boolean enabled) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateExpansionState, enabled: %b", TAG, enabled);
+ int actionIndex = mActionsList.indexOf(mExpandCollapseAction);
+ boolean actionInList = actionIndex != -1;
+ if (enabled && !actionInList) {
+ mActionsList.add(mExpandCollapseAction);
+ actionIndex = mActionsList.size() - 1;
+ } else if (!enabled && actionInList) {
+ mActionsList.remove(actionIndex);
+ } else {
+ return;
+ }
+ notifyActionsChanged(/* added= */ enabled ? 1 : -1, /* updated= */ 0, actionIndex);
+ }
+
+ @VisibleForTesting(visibility = PACKAGE)
+ public void onPipExpansionToggled(boolean expanded) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipExpansionToggled, expanded: %b", TAG, expanded);
+
+ mExpandCollapseAction.update(
+ expanded ? R.string.pip_collapse : R.string.pip_expand,
+ expanded ? R.drawable.pip_ic_collapse : R.drawable.pip_ic_expand);
+
+ notifyActionsChanged(/* added= */ 0, /* updated= */ 1,
+ mActionsList.indexOf(mExpandCollapseAction));
+ }
+
+ List<TvPipAction> getActionsList() {
+ return mActionsList;
+ }
+
+ @NonNull
+ TvPipAction getCloseAction() {
+ return mActionsList.get(CLOSE_ACTION_INDEX);
+ }
+
+ void addListener(Listener listener) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Returns the index of the first action of the given action type or -1 if none can be found.
+ */
+ int getFirstIndexOfAction(@TvPipAction.ActionType int actionType) {
+ for (int i = 0; i < mActionsList.size(); i++) {
+ if (mActionsList.get(i).getActionType() == actionType) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Allow components to listen to updates to the actions list, including where they happen so
+ * that changes can be animated.
+ */
+ interface Listener {
+ /**
+ * Notifies the listener how many actions were added/removed or updated.
+ *
+ * @param added can be positive (number of actions added), negative (number of actions
+ * removed) or zero (the number of actions stayed the same).
+ * @param updated the number of actions that might have been updated and need to be
+ * refreshed.
+ * @param startIndex The index of the first updated action. The added/removed actions start
+ * at (startIndex + updated).
+ */
+ void onActionsChanged(int added, int updated, int startIndex);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index ce34d2f9547d..31490e427a53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -22,14 +22,12 @@ import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_HORIZONTAL;
-import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_UNDETERMINED;
import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_VERTICAL;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.util.ArraySet;
import android.util.Size;
import android.view.Gravity;
@@ -50,11 +48,10 @@ import java.util.Set;
* Contains pip bounds calculations that are specific to TV.
*/
public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
-
private static final String TAG = TvPipBoundsAlgorithm.class.getSimpleName();
- private static final boolean DEBUG = TvPipController.DEBUG;
- private final @NonNull TvPipBoundsState mTvPipBoundsState;
+ @NonNull
+ private final TvPipBoundsState mTvPipBoundsState;
private int mFixedExpandedHeightInPx;
private int mFixedExpandedWidthInPx;
@@ -65,7 +62,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
@NonNull TvPipBoundsState tvPipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
super(context, tvPipBoundsState, pipSnapAlgorithm,
- new PipKeepClearAlgorithm() {});
+ new PipKeepClearAlgorithm() {
+ });
this.mTvPipBoundsState = tvPipBoundsState;
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
reloadResources(context);
@@ -92,16 +90,15 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
/** Returns the destination bounds to place the PIP window on entry. */
@Override
public Rect getEntryDestinationBounds() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getEntryDestinationBounds()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getEntryDestinationBounds()", TAG);
+
updateExpandedPipSize();
final boolean isPipExpanded = mTvPipBoundsState.isTvExpandedPipSupported()
&& mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0
&& !mTvPipBoundsState.isTvPipManuallyCollapsed();
if (isPipExpanded) {
- updateGravityOnExpandToggled(Gravity.NO_GRAVITY, true);
+ updateGravityOnExpansionToggled(/* expanding= */ true);
}
mTvPipBoundsState.setTvPipExpanded(isPipExpanded);
return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
@@ -110,10 +107,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
@Override
public Rect getAdjustedDestinationBounds(Rect currentBounds, float newAspectRatio) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
}
@@ -141,25 +136,9 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- Set<Rect> restrictedKeepClearAreas = mTvPipBoundsState.getRestrictedKeepClearAreas();
- Set<Rect> unrestrictedKeepClearAreas = mTvPipBoundsState.getUnrestrictedKeepClearAreas();
-
- if (mTvPipBoundsState.isImeShowing()) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: IME showing, height: %d",
- TAG, mTvPipBoundsState.getImeHeight());
- }
-
- final Rect imeBounds = new Rect(
- 0,
- insetBounds.bottom - mTvPipBoundsState.getImeHeight(),
- insetBounds.right,
- insetBounds.bottom);
-
- unrestrictedKeepClearAreas = new ArraySet<>(unrestrictedKeepClearAreas);
- unrestrictedKeepClearAreas.add(imeBounds);
- }
+ final Set<Rect> restrictedKeepClearAreas = mTvPipBoundsState.getRestrictedKeepClearAreas();
+ final Set<Rect> unrestrictedKeepClearAreas =
+ mTvPipBoundsState.getUnrestrictedKeepClearAreas();
mKeepClearAlgorithm.setGravity(mTvPipBoundsState.getTvPipGravity());
mKeepClearAlgorithm.setScreenSize(screenSize);
@@ -173,165 +152,105 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
restrictedKeepClearAreas,
unrestrictedKeepClearAreas);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: screenSize: %s", TAG, screenSize);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: stashOffset: %d", TAG, mTvPipBoundsState.getStashOffset());
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: insetBounds: %s", TAG, insetBounds.toShortString());
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: pipSize: %s", TAG, pipSize);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: gravity: %s", TAG, Gravity.toString(mTvPipBoundsState.getTvPipGravity()));
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: restrictedKeepClearAreas: %s", TAG, restrictedKeepClearAreas);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: unrestrictedKeepClearAreas: %s", TAG, unrestrictedKeepClearAreas);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: placement: %s", TAG, placement);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: screenSize: %s", TAG, screenSize);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: stashOffset: %d", TAG, mTvPipBoundsState.getStashOffset());
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: insetBounds: %s", TAG, insetBounds.toShortString());
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: pipSize: %s", TAG, pipSize);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: gravity: %s", TAG, Gravity.toString(mTvPipBoundsState.getTvPipGravity()));
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: restrictedKeepClearAreas: %s", TAG, restrictedKeepClearAreas);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: unrestrictedKeepClearAreas: %s", TAG, unrestrictedKeepClearAreas);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: placement: %s", TAG, placement);
return placement;
}
- /**
- * @return previous gravity if it is to be saved, or {@link Gravity#NO_GRAVITY} if not.
- */
- int updateGravityOnExpandToggled(int previousGravity, boolean expanding) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateGravityOnExpandToggled(), expanding: %b"
- + ", mOrientation: %d, previous gravity: %s",
- TAG, expanding, mTvPipBoundsState.getTvFixedPipOrientation(),
- Gravity.toString(previousGravity));
- }
-
- if (!mTvPipBoundsState.isTvExpandedPipSupported()) {
- return Gravity.NO_GRAVITY;
- }
+ void updateGravityOnExpansionToggled(boolean expanding) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateGravity, expanding: %b, fixedExpandedOrientation: %d",
+ TAG, expanding, mTvPipBoundsState.getTvFixedPipOrientation());
- if (expanding && mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_UNDETERMINED) {
- float expandedRatio = mTvPipBoundsState.getDesiredTvExpandedAspectRatio();
- if (expandedRatio == 0) {
- return Gravity.NO_GRAVITY;
- }
- if (expandedRatio < 1) {
- mTvPipBoundsState.setTvFixedPipOrientation(ORIENTATION_VERTICAL);
- } else {
- mTvPipBoundsState.setTvFixedPipOrientation(ORIENTATION_HORIZONTAL);
- }
- }
+ int currentX = mTvPipBoundsState.getTvPipGravity() & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int currentY = mTvPipBoundsState.getTvPipGravity() & Gravity.VERTICAL_GRAVITY_MASK;
+ int previousCollapsedX = mTvPipBoundsState.getTvPipPreviousCollapsedGravity()
+ & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int previousCollapsedY = mTvPipBoundsState.getTvPipPreviousCollapsedGravity()
+ & Gravity.VERTICAL_GRAVITY_MASK;
- int gravityToSave = Gravity.NO_GRAVITY;
- int currentGravity = mTvPipBoundsState.getTvPipGravity();
int updatedGravity;
-
if (expanding) {
- // save collapsed gravity
- gravityToSave = mTvPipBoundsState.getTvPipGravity();
+ // Save collapsed gravity.
+ mTvPipBoundsState.setTvPipPreviousCollapsedGravity(mTvPipBoundsState.getTvPipGravity());
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
- updatedGravity =
- Gravity.CENTER_HORIZONTAL | (currentGravity
- & Gravity.VERTICAL_GRAVITY_MASK);
+ updatedGravity = Gravity.CENTER_HORIZONTAL | currentY;
} else {
- updatedGravity =
- Gravity.CENTER_VERTICAL | (currentGravity
- & Gravity.HORIZONTAL_GRAVITY_MASK);
+ updatedGravity = currentX | Gravity.CENTER_VERTICAL;
}
} else {
- if (previousGravity != Gravity.NO_GRAVITY) {
- // The pip hasn't been moved since expanding,
- // go back to previous collapsed position.
- updatedGravity = previousGravity;
+ // Collapse to the edge that the user moved to before.
+ if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
+ updatedGravity = previousCollapsedX | currentY;
} else {
- if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
- updatedGravity =
- Gravity.RIGHT | (currentGravity & Gravity.VERTICAL_GRAVITY_MASK);
- } else {
- updatedGravity =
- Gravity.BOTTOM | (currentGravity & Gravity.HORIZONTAL_GRAVITY_MASK);
- }
+ updatedGravity = currentX | previousCollapsedY;
}
}
mTvPipBoundsState.setTvPipGravity(updatedGravity);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
- }
-
- return gravityToSave;
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
}
/**
- * @return true if gravity changed
+ * @return true if the gravity changed
*/
boolean updateGravity(int keycode) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateGravity, keycode: %d", TAG, keycode);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateGravity, keycode: %d", TAG, keycode);
- // Check if position change is valid
+ // Check if position change is valid.
if (mTvPipBoundsState.isTvPipExpanded()) {
- int mOrientation = mTvPipBoundsState.getTvFixedPipOrientation();
- if (mOrientation == ORIENTATION_VERTICAL
+ int fixedOrientation = mTvPipBoundsState.getTvFixedPipOrientation();
+ if (fixedOrientation == ORIENTATION_VERTICAL
&& (keycode == KEYCODE_DPAD_UP || keycode == KEYCODE_DPAD_DOWN)
- || mOrientation == ORIENTATION_HORIZONTAL
+ || fixedOrientation == ORIENTATION_HORIZONTAL
&& (keycode == KEYCODE_DPAD_RIGHT || keycode == KEYCODE_DPAD_LEFT)) {
return false;
}
}
- int currentGravity = mTvPipBoundsState.getTvPipGravity();
- int updatedGravity;
- // First axis
+ int updatedX = mTvPipBoundsState.getTvPipGravity() & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int updatedY = mTvPipBoundsState.getTvPipGravity() & Gravity.VERTICAL_GRAVITY_MASK;
+
switch (keycode) {
case KEYCODE_DPAD_UP:
- updatedGravity = Gravity.TOP;
+ updatedY = Gravity.TOP;
break;
case KEYCODE_DPAD_DOWN:
- updatedGravity = Gravity.BOTTOM;
+ updatedY = Gravity.BOTTOM;
break;
case KEYCODE_DPAD_LEFT:
- updatedGravity = Gravity.LEFT;
+ updatedX = Gravity.LEFT;
break;
case KEYCODE_DPAD_RIGHT:
- updatedGravity = Gravity.RIGHT;
+ updatedX = Gravity.RIGHT;
break;
default:
- updatedGravity = currentGravity;
+ // NOOP - unsupported keycode
}
- // Second axis
- switch (keycode) {
- case KEYCODE_DPAD_UP:
- case KEYCODE_DPAD_DOWN:
- if (mTvPipBoundsState.isTvPipExpanded()) {
- updatedGravity |= Gravity.CENTER_HORIZONTAL;
- } else {
- updatedGravity |= (currentGravity & Gravity.HORIZONTAL_GRAVITY_MASK);
- }
- break;
- case KEYCODE_DPAD_LEFT:
- case KEYCODE_DPAD_RIGHT:
- if (mTvPipBoundsState.isTvPipExpanded()) {
- updatedGravity |= Gravity.CENTER_VERTICAL;
- } else {
- updatedGravity |= (currentGravity & Gravity.VERTICAL_GRAVITY_MASK);
- }
- break;
- default:
- break;
- }
+ int updatedGravity = updatedX | updatedY;
- if (updatedGravity != currentGravity) {
+ if (updatedGravity != mTvPipBoundsState.getTvPipGravity()) {
mTvPipBoundsState.setTvPipGravity(updatedGravity);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: new gravity: %s", TAG, Gravity.toString(updatedGravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateGravity, new gravity: %s", TAG, Gravity.toString(updatedGravity));
return true;
}
return false;
@@ -362,8 +281,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
final Size expandedSize;
if (expandedRatio == 0) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateExpandedPipSize(): Expanded mode aspect ratio"
- + " of 0 not supported", TAG);
+ "%s: updateExpandedPipSize(): Expanded mode aspect ratio"
+ + " of 0 not supported", TAG);
return;
} else if (expandedRatio < 1) {
// vertical
@@ -375,16 +294,12 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
if (maxHeight > aspectRatioHeight) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Accommodate aspect ratio", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Accommodate aspect ratio", TAG);
expandedSize = new Size(mFixedExpandedWidthInPx, (int) aspectRatioHeight);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Aspect ratio is too extreme, use max size", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Aspect ratio is too extreme, use max size", TAG);
expandedSize = new Size(mFixedExpandedWidthInPx, maxHeight);
}
}
@@ -397,26 +312,20 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
- pipDecorations.left - pipDecorations.right;
float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
if (maxWidth > aspectRatioWidth) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Accommodate aspect ratio", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Accommodate aspect ratio", TAG);
expandedSize = new Size((int) aspectRatioWidth, mFixedExpandedHeightInPx);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Aspect ratio is too extreme, use max size", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Aspect ratio is too extreme, use max size", TAG);
expandedSize = new Size(maxWidth, mFixedExpandedHeightInPx);
}
}
}
mTvPipBoundsState.setTvExpandedSize(expandedSize);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateExpandedPipSize(): expanded size, width: %d, height: %d",
- TAG, expandedSize.getWidth(), expandedSize.getHeight());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateExpandedPipSize(): expanded size, width: %d, height: %d",
+ TAG, expandedSize.getWidth(), expandedSize.getHeight());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81821ec..b189163a354a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -39,7 +39,6 @@ import java.util.function.Supplier;
* Manages debouncing of PiP movements and scheduling of unstashing.
*/
public class TvPipBoundsController {
- private static final boolean DEBUG = false;
private static final String TAG = "TvPipBoundsController";
/**
@@ -122,9 +121,9 @@ public class TvPipBoundsController {
cancelScheduledPlacement();
applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
} else if (immediate) {
+ boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
cancelScheduledPlacement();
- applyPlacementBounds(placement.getBounds(), animationDuration);
- scheduleUnstashIfNeeded(placement);
+ applyPlacement(placement, shouldStash, animationDuration);
} else {
applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
schedulePinnedStackPlacement(placement, animationDuration);
@@ -133,11 +132,9 @@ public class TvPipBoundsController {
private void schedulePinnedStackPlacement(@NonNull final Placement placement,
int animationDuration) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: schedulePinnedStackPlacement() - pip bounds: %s",
- TAG, placement.getBounds().toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: schedulePinnedStackPlacement() - pip bounds: %s",
+ TAG, placement.getBounds().toShortString());
if (mPendingPlacement != null && Objects.equals(mPendingPlacement.getBounds(),
placement.getBounds())) {
@@ -171,27 +168,24 @@ public class TvPipBoundsController {
}
private void applyPendingPlacement() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: applyPendingPlacement()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: applyPendingPlacement()", TAG);
if (mPendingPlacement != null) {
- if (mPendingStash) {
- mPendingStash = false;
- scheduleUnstashIfNeeded(mPendingPlacement);
- }
-
- if (mUnstashRunnable != null) {
- // currently stashed, use stashed pos
- applyPlacementBounds(mPendingPlacement.getBounds(),
- mPendingPlacementAnimationDuration);
- } else {
- applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
- mPendingPlacementAnimationDuration);
- }
+ applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+ mPendingStash = false;
+ mPendingPlacement = null;
}
+ }
- mPendingPlacement = null;
+ private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+ int animationDuration) {
+ if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+ scheduleUnstashIfNeeded(placement);
+ }
+
+ Rect bounds =
+ mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+ applyPlacementBounds(bounds, animationDuration);
}
void onPipDismissed() {
@@ -227,10 +221,8 @@ public class TvPipBoundsController {
}
mPipTargetBounds = bounds;
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipTo() - new pip bounds: %s", TAG, bounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipTo() - new pip bounds: %s", TAG, bounds.toShortString());
if (mListener != null) {
mListener.onPipTargetBoundsChange(bounds, animationDuration);
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 ca22882187d8..9c7c0aef636d 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
@@ -27,6 +27,7 @@ import android.content.pm.PackageManager;
import android.graphics.Insets;
import android.util.Size;
import android.view.Gravity;
+import android.view.View;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -52,24 +53,61 @@ public class TvPipBoundsState extends PipBoundsState {
public @interface Orientation {
}
- public static final int DEFAULT_TV_GRAVITY = Gravity.BOTTOM | Gravity.RIGHT;
+ private final Context mContext;
+
+ private int mDefaultGravity;
+ private int mTvPipGravity;
+ private int mPreviousCollapsedGravity;
+ private boolean mIsRtl;
private final boolean mIsTvExpandedPipSupported;
private boolean mIsTvPipExpanded;
private boolean mTvPipManuallyCollapsed;
private float mDesiredTvExpandedAspectRatio;
- private @Orientation int mTvFixedPipOrientation;
- private int mTvPipGravity;
- private @Nullable Size mTvExpandedSize;
- private @NonNull Insets mPipMenuPermanentDecorInsets = Insets.NONE;
- private @NonNull Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
+ @Orientation
+ private int mTvFixedPipOrientation;
+ @Nullable
+ private Size mTvExpandedSize;
+ @NonNull
+ private Insets mPipMenuPermanentDecorInsets = Insets.NONE;
+ @NonNull
+ private Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
public TvPipBoundsState(@NonNull Context context) {
super(context);
+ mContext = context;
+ updateDefaultGravity();
+ mPreviousCollapsedGravity = mDefaultGravity;
mIsTvExpandedPipSupported = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE);
}
+ public int getDefaultGravity() {
+ return mDefaultGravity;
+ }
+
+ private void updateDefaultGravity() {
+ boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ mDefaultGravity = Gravity.BOTTOM | (isRtl ? Gravity.LEFT : Gravity.RIGHT);
+
+ if (mIsRtl != isRtl) {
+ int prevGravityX = mPreviousCollapsedGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int prevGravityY = mPreviousCollapsedGravity & Gravity.VERTICAL_GRAVITY_MASK;
+ if ((prevGravityX & Gravity.RIGHT) == Gravity.RIGHT) {
+ mPreviousCollapsedGravity = Gravity.LEFT | prevGravityY;
+ } else if ((prevGravityX & Gravity.LEFT) == Gravity.LEFT) {
+ mPreviousCollapsedGravity = Gravity.RIGHT | prevGravityY;
+ }
+ }
+ mIsRtl = isRtl;
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ updateDefaultGravity();
+ }
+
/**
* Initialize states when first entering PiP.
*/
@@ -86,7 +124,9 @@ public class TvPipBoundsState extends PipBoundsState {
/** Resets the TV PiP state for a new activity. */
public void resetTvPipState() {
mTvFixedPipOrientation = ORIENTATION_UNDETERMINED;
- mTvPipGravity = DEFAULT_TV_GRAVITY;
+ mTvPipGravity = mDefaultGravity;
+ mPreviousCollapsedGravity = mDefaultGravity;
+ mTvPipManuallyCollapsed = false;
}
/** Set the tv expanded bounds of PiP */
@@ -101,16 +141,23 @@ public class TvPipBoundsState extends PipBoundsState {
}
/** Set the PiP aspect ratio for the expanded PiP (TV) that is desired by the app. */
- public void setDesiredTvExpandedAspectRatio(float aspectRatio, boolean override) {
+ public void setDesiredTvExpandedAspectRatio(float expandedAspectRatio, boolean override) {
if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED) {
- mDesiredTvExpandedAspectRatio = aspectRatio;
resetTvPipState();
+ mDesiredTvExpandedAspectRatio = expandedAspectRatio;
+ if (expandedAspectRatio != 0) {
+ if (expandedAspectRatio > 1) {
+ mTvFixedPipOrientation = ORIENTATION_HORIZONTAL;
+ } else {
+ mTvFixedPipOrientation = ORIENTATION_VERTICAL;
+ }
+ }
return;
}
- if ((aspectRatio > 1 && mTvFixedPipOrientation == ORIENTATION_HORIZONTAL)
- || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL)
- || aspectRatio == 0) {
- mDesiredTvExpandedAspectRatio = aspectRatio;
+ if ((expandedAspectRatio > 1 && mTvFixedPipOrientation == ORIENTATION_HORIZONTAL)
+ || (expandedAspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL)
+ || expandedAspectRatio == 0) {
+ mDesiredTvExpandedAspectRatio = expandedAspectRatio;
}
}
@@ -122,11 +169,6 @@ public class TvPipBoundsState extends PipBoundsState {
return mDesiredTvExpandedAspectRatio;
}
- /** Sets the orientation the expanded TV PiP activity has been fixed to. */
- public void setTvFixedPipOrientation(@Orientation int orientation) {
- mTvFixedPipOrientation = orientation;
- }
-
/** Returns the fixed orientation of the expanded PiP on TV. */
@Orientation
public int getTvFixedPipOrientation() {
@@ -143,6 +185,14 @@ public class TvPipBoundsState extends PipBoundsState {
return mTvPipGravity;
}
+ public void setTvPipPreviousCollapsedGravity(int gravity) {
+ mPreviousCollapsedGravity = gravity;
+ }
+
+ public int getTvPipPreviousCollapsedGravity() {
+ return mPreviousCollapsedGravity;
+ }
+
/** Sets whether the TV PiP is currently expanded. */
public void setTvPipExpanded(boolean expanded) {
mIsTvPipExpanded = expanded;
@@ -172,7 +222,8 @@ public class TvPipBoundsState extends PipBoundsState {
mPipMenuPermanentDecorInsets = permanentInsets;
}
- public @NonNull Insets getPipMenuPermanentDecorInsets() {
+ @NonNull
+ public Insets getPipMenuPermanentDecorInsets() {
return mPipMenuPermanentDecorInsets;
}
@@ -180,7 +231,8 @@ public class TvPipBoundsState extends PipBoundsState {
mPipMenuTemporaryDecorInsets = temporaryDecorInsets;
}
- public @NonNull Insets getPipMenuTemporaryDecorInsets() {
+ @NonNull
+ public Insets getPipMenuTemporaryDecorInsets() {
return mPipMenuTemporaryDecorInsets;
}
}
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 4e1b0469eb96..fd4fcff54f01 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
@@ -18,17 +18,22 @@ package com.android.wm.shell.pip.tv;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.TaskInfo;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
import android.view.Gravity;
@@ -67,10 +72,9 @@ import java.util.Set;
*/
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
- TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener,
- ConfigurationChangeListener, UserChangeListener {
+ DisplayController.OnDisplaysChangedListener, ConfigurationChangeListener,
+ UserChangeListener {
private static final String TAG = "TvPipController";
- static final boolean DEBUG = false;
private static final int NONEXISTENT_TASK_ID = -1;
@@ -80,7 +84,8 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
STATE_PIP,
STATE_PIP_MENU,
})
- public @interface State {}
+ public @interface State {
+ }
/**
* State when there is no applications in Pip.
@@ -98,6 +103,17 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
*/
private static final int STATE_PIP_MENU = 2;
+ static final String ACTION_SHOW_PIP_MENU =
+ "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
+ static final String ACTION_CLOSE_PIP =
+ "com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
+ static final String ACTION_MOVE_PIP =
+ "com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
+ static final String ACTION_TOGGLE_EXPANDED_PIP =
+ "com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
+ static final String ACTION_TO_FULLSCREEN =
+ "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
+
private final Context mContext;
private final ShellController mShellController;
@@ -107,6 +123,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final PipAppOpsListener mAppOpsListener;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
+ private final TvPipActionsProvider mTvPipActionsProvider;
private final TvPipNotificationController mPipNotificationController;
private final TvPipMenuController mTvPipMenuController;
private final PipTransitionController mPipTransitionController;
@@ -115,18 +132,20 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final DisplayController mDisplayController;
private final WindowManagerShellWrapper mWmShellWrapper;
private final ShellExecutor mMainExecutor;
+ private final Handler mMainHandler; // For registering the broadcast receiver
private final TvPipImpl mImpl = new TvPipImpl();
- private @State int mState = STATE_NO_PIP;
- private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
+ private final ActionBroadcastReceiver mActionBroadcastReceiver;
+
+ @State
+ private int mState = STATE_NO_PIP;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
- private RemoteAction mCloseAction;
// How long the shell will wait for the app to close the PiP if a custom action is set.
private int mPipForceCloseDelay;
private int mResizeAnimationDuration;
- private int mEduTextWindowExitAnimationDurationMs;
+ private int mEduTextWindowExitAnimationDuration;
public static Pip create(
Context context,
@@ -145,6 +164,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShell,
+ Handler mainHandler,
ShellExecutor mainExecutor) {
return new TvPipController(
context,
@@ -163,6 +183,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
pipParamsChangedForwarder,
displayController,
wmShell,
+ mainHandler,
mainExecutor).mImpl;
}
@@ -183,8 +204,10 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShellWrapper,
+ Handler mainHandler,
ShellExecutor mainExecutor) {
mContext = context;
+ mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
mShellController = shellController;
mDisplayController = displayController;
@@ -192,17 +215,23 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mTvPipBoundsState = tvPipBoundsState;
mTvPipBoundsState.setDisplayId(context.getDisplayId());
mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
+
mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mTvPipBoundsController = tvPipBoundsController;
mTvPipBoundsController.setListener(this);
mPipMediaController = pipMediaController;
+ mTvPipActionsProvider = new TvPipActionsProvider(context, pipMediaController,
+ this::executeAction);
mPipNotificationController = pipNotificationController;
- mPipNotificationController.setDelegate(this);
+ mPipNotificationController.setTvPipActionsProvider(mTvPipActionsProvider);
mTvPipMenuController = tvPipMenuController;
mTvPipMenuController.setDelegate(this);
+ mTvPipMenuController.setTvPipActionsProvider(mTvPipActionsProvider);
+
+ mActionBroadcastReceiver = new ActionBroadcastReceiver();
mAppOpsListener = pipAppOpsListener;
mPipTaskOrganizer = pipTaskOrganizer;
@@ -216,7 +245,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private void onInit() {
mPipTransitionController.registerPipTransitionCallback(this);
- loadConfigurations();
+ reloadResources();
registerPipParamsChangedListener(mPipParamsChangedForwarder);
registerTaskStackListenerCallback(mTaskStackListener);
@@ -236,22 +265,41 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState));
- if (isPipShown()) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: > closing Pip.", TAG);
- }
- closePip();
- }
+ int previousDefaultGravityX = mTvPipBoundsState.getDefaultGravity()
+ & Gravity.HORIZONTAL_GRAVITY_MASK;
- loadConfigurations();
- mPipNotificationController.onConfigurationChanged(mContext);
+ reloadResources();
+
+ mPipNotificationController.onConfigurationChanged();
mTvPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ mTvPipBoundsState.onConfigurationChanged();
+
+ int defaultGravityX = mTvPipBoundsState.getDefaultGravity()
+ & Gravity.HORIZONTAL_GRAVITY_MASK;
+ if (isPipShown() && previousDefaultGravityX != defaultGravityX) {
+ movePipToOppositeSide();
+ }
+ }
+
+ private void reloadResources() {
+ final Resources res = mContext.getResources();
+ mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mPipForceCloseDelay = res.getInteger(R.integer.config_pipForceCloseDelay);
+ mEduTextWindowExitAnimationDuration =
+ res.getInteger(R.integer.pip_edu_text_window_exit_animation_duration);
+ }
+
+ private void movePipToOppositeSide() {
+ ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipToOppositeSide", TAG);
+ if ((mTvPipBoundsState.getTvPipGravity() & Gravity.RIGHT) == Gravity.RIGHT) {
+ movePip(KEYCODE_DPAD_LEFT);
+ } else if ((mTvPipBoundsState.getTvPipGravity() & Gravity.LEFT) == Gravity.LEFT) {
+ movePip(KEYCODE_DPAD_RIGHT);
+ }
}
/**
@@ -265,33 +313,32 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
* Starts the process if bringing up the Pip menu if by issuing a command to move Pip
* task/window to the "Menu" position. We'll show the actual Menu UI (eg. actions) once the Pip
* task/window is properly positioned in {@link #onPipTransitionFinished(int)}.
+ *
+ * @param moveMenu If true, show the moveMenu, otherwise show the regular menu.
*/
- @Override
- public void showPictureInPictureMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showPictureInPictureMenu(), state=%s", TAG, stateToName(mState));
- }
+ private void showPictureInPictureMenu(boolean moveMenu) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showPictureInPictureMenu(), state=%s", TAG, stateToName(mState));
if (mState == STATE_NO_PIP) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: > cannot open Menu from the current state.", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: > cannot open Menu from the current state.", TAG);
return;
}
setState(STATE_PIP_MENU);
- mTvPipMenuController.showMenu();
+ if (moveMenu) {
+ mTvPipMenuController.showMovementMenu();
+ } else {
+ mTvPipMenuController.showMenu();
+ }
updatePinnedStackBounds();
}
@Override
public void onMenuClosed() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
setState(STATE_PIP);
updatePinnedStackBounds();
}
@@ -304,66 +351,37 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
/**
* Opens the "Pip-ed" Activity fullscreen.
*/
- @Override
- public void movePipToFullscreen() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
- }
+ private void movePipToFullscreen() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipToFullscreen(), state=%s", TAG, stateToName(mState));
mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared();
}
- @Override
- public void togglePipExpansion() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: togglePipExpansion()", TAG);
- }
+ private void togglePipExpansion() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: togglePipExpansion()", TAG);
boolean expanding = !mTvPipBoundsState.isTvPipExpanded();
- int saveGravity = mTvPipBoundsAlgorithm
- .updateGravityOnExpandToggled(mPreviousGravity, expanding);
- if (saveGravity != Gravity.NO_GRAVITY) {
- mPreviousGravity = saveGravity;
- }
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(expanding);
mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
mTvPipBoundsState.setTvPipExpanded(expanding);
- mPipNotificationController.updateExpansionState();
updatePinnedStackBounds();
}
@Override
- public void enterPipMovementMenu() {
- setState(STATE_PIP_MENU);
- mTvPipMenuController.showMovementMenuOnly();
- }
-
- @Override
public void movePip(int keycode) {
if (mTvPipBoundsAlgorithm.updateGravity(keycode)) {
mTvPipMenuController.updateGravity(mTvPipBoundsState.getTvPipGravity());
- mPreviousGravity = Gravity.NO_GRAVITY;
updatePinnedStackBounds();
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Position hasn't changed", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Position hasn't changed", TAG);
}
}
@Override
- public int getPipGravity() {
- return mTvPipBoundsState.getTvPipGravity();
- }
-
- public int getOrientation() {
- return mTvPipBoundsState.getTvFixedPipOrientation();
- }
-
- @Override
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {
if (mTvPipBoundsState.getDisplayId() == displayId) {
@@ -392,34 +410,25 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
@Override
- public void onPipTargetBoundsChange(Rect newTargetBounds, int animationDuration) {
- mPipTaskOrganizer.scheduleAnimateResizePip(newTargetBounds,
- animationDuration, rect -> mTvPipMenuController.updateExpansionState());
- mTvPipMenuController.onPipTransitionStarted(newTargetBounds);
+ public void onPipTargetBoundsChange(Rect targetBounds, int animationDuration) {
+ mPipTaskOrganizer.scheduleAnimateResizePip(targetBounds,
+ animationDuration, null);
+ mTvPipMenuController.onPipTransitionToTargetBoundsStarted(targetBounds);
}
/**
* Closes Pip window.
*/
- @Override
public void closePip() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closePip(), state=%s, loseAction=%s", TAG, stateToName(mState),
- mCloseAction);
- }
+ closeCurrentPiP(mPinnedTaskId);
+ }
- if (mCloseAction != null) {
- try {
- mCloseAction.getActionIntent().send();
- } catch (PendingIntent.CanceledException e) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Failed to send close action, %s", TAG, e);
- }
- mMainExecutor.executeDelayed(() -> closeCurrentPiP(mPinnedTaskId), mPipForceCloseDelay);
- } else {
- closeCurrentPiP(mPinnedTaskId);
- }
+ /**
+ * Force close the current PiP after some time in case the custom action hasn't done it by
+ * itself.
+ */
+ public void customClosePip() {
+ mMainExecutor.executeDelayed(() -> closeCurrentPiP(mPinnedTaskId), mPipForceCloseDelay);
}
private void closeCurrentPiP(int pinnedTaskId) {
@@ -434,7 +443,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void closeEduText() {
- updatePinnedStackBounds(mEduTextWindowExitAnimationDurationMs, false);
+ updatePinnedStackBounds(mEduTextWindowExitAnimationDuration, false);
}
private void registerSessionListenerForCurrentUser() {
@@ -443,22 +452,19 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private void checkIfPinnedTaskAppeared() {
final TaskInfo pinnedTask = getPinnedTaskInfo();
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: checkIfPinnedTaskAppeared(), task=%s", TAG, pinnedTask);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: checkIfPinnedTaskAppeared(), task=%s", TAG, pinnedTask);
if (pinnedTask == null || pinnedTask.topActivity == null) return;
mPinnedTaskId = pinnedTask.taskId;
mPipMediaController.onActivityPinned();
+ mActionBroadcastReceiver.register();
mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
}
private void checkIfPinnedTaskIsGone() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onTaskStackChanged()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onTaskStackChanged()", TAG);
if (isPipShown() && getPinnedTaskInfo() == null) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -468,12 +474,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
private void onPipDisappeared() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipDisappeared() state=%s", TAG, stateToName(mState));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipDisappeared() state=%s", TAG, stateToName(mState));
mPipNotificationController.dismiss();
+ mActionBroadcastReceiver.unregister();
+
mTvPipMenuController.closeMenu();
mTvPipBoundsState.resetTvPipState();
mTvPipBoundsController.onPipDismissed();
@@ -482,50 +488,49 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
@Override
- public void onPipTransitionStarted(int direction, Rect pipBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Started(), state=%s", TAG, stateToName(mState));
+ public void onPipTransitionStarted(int direction, Rect currentPipBounds) {
+ final boolean enterPipTransition = PipAnimationController.isInPipDirection(direction);
+ if (enterPipTransition && mState == STATE_NO_PIP) {
+ // Set the initial ability to expand the PiP when entering PiP.
+ updateExpansionState();
}
- mTvPipMenuController.notifyPipAnimating(true);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Started(), state=%s, direction=%d",
+ TAG, stateToName(mState), direction);
}
@Override
public void onPipTransitionCanceled(int direction) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Canceled(), state=%s", TAG, stateToName(mState));
- }
- mTvPipMenuController.notifyPipAnimating(false);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Canceled(), state=%s", TAG, stateToName(mState));
+ mTvPipMenuController.onPipTransitionFinished(
+ PipAnimationController.isInPipDirection(direction));
+ mTvPipActionsProvider.onPipExpansionToggled(mTvPipBoundsState.isTvPipExpanded());
}
@Override
public void onPipTransitionFinished(int direction) {
- if (PipAnimationController.isInPipDirection(direction) && mState == STATE_NO_PIP) {
+ final boolean enterPipTransition = PipAnimationController.isInPipDirection(direction);
+ if (enterPipTransition && mState == STATE_NO_PIP) {
setState(STATE_PIP);
}
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransition_Finished(), state=%s", TAG, stateToName(mState));
- }
- mTvPipMenuController.notifyPipAnimating(false);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransition_Finished(), state=%s, direction=%d",
+ TAG, stateToName(mState), direction);
+ mTvPipMenuController.onPipTransitionFinished(enterPipTransition);
+ mTvPipActionsProvider.onPipExpansionToggled(mTvPipBoundsState.isTvPipExpanded());
}
- private void setState(@State int state) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setState(), state=%s, prev=%s",
- TAG, stateToName(state), stateToName(mState));
- }
- mState = state;
+ private void updateExpansionState() {
+ mTvPipActionsProvider.updateExpansionEnabled(mTvPipBoundsState.isTvExpandedPipSupported()
+ && mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0);
}
- private void loadConfigurations() {
- final Resources res = mContext.getResources();
- mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipForceCloseDelay = res.getInteger(R.integer.config_pipForceCloseDelay);
- mEduTextWindowExitAnimationDurationMs =
- res.getInteger(R.integer.pip_edu_text_window_exit_animation_duration_ms);
+ private void setState(@State int state) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setState(), state=%s, prev=%s",
+ TAG, stateToName(state), stateToName(mState));
+ mState = state;
}
private void registerTaskStackListenerCallback(TaskStackListenerImpl taskStackListener) {
@@ -550,11 +555,8 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
if (task.getWindowingMode() == WINDOWING_MODE_PINNED) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPinnedActivityRestartAttempt()", TAG);
- }
-
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPinnedActivityRestartAttempt()", TAG);
// If the "Pip-ed" Activity is launched again by Launcher or intent, make it
// fullscreen.
movePipToFullscreen();
@@ -569,16 +571,15 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void onActionsChanged(List<RemoteAction> actions,
RemoteAction closeAction) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onActionsChanged()", TAG);
+ "%s: onActionsChanged()", TAG);
- mTvPipMenuController.setAppActions(actions, closeAction);
- mCloseAction = closeAction;
+ mTvPipActionsProvider.setAppActions(actions, closeAction);
}
@Override
public void onAspectRatioChanged(float ratio) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onAspectRatioChanged: %f", TAG, ratio);
+ "%s: onAspectRatioChanged: %f", TAG, ratio);
mTvPipBoundsState.setAspectRatio(ratio);
if (!mTvPipBoundsState.isTvPipExpanded()) {
@@ -589,10 +590,10 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void onExpandedAspectRatioChanged(float ratio) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
+ "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
- mTvPipMenuController.updateExpansionState();
+ updateExpansionState();
// 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
// --> update bounds, but don't toggle
@@ -604,11 +605,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
// 2) PiP is expanded, but expanded PiP was disabled
// --> collapse PiP
if (mTvPipBoundsState.isTvPipExpanded() && ratio == 0) {
- int saveGravity = mTvPipBoundsAlgorithm
- .updateGravityOnExpandToggled(mPreviousGravity, false);
- if (saveGravity != Gravity.NO_GRAVITY) {
- mPreviousGravity = saveGravity;
- }
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(/* expanding= */ false);
mTvPipBoundsState.setTvPipExpanded(false);
updatePinnedStackBounds();
}
@@ -618,11 +615,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
if (!mTvPipBoundsState.isTvPipExpanded() && ratio != 0
&& !mTvPipBoundsState.isTvPipManuallyCollapsed()) {
mTvPipBoundsAlgorithm.updateExpandedPipSize();
- int saveGravity = mTvPipBoundsAlgorithm
- .updateGravityOnExpandToggled(mPreviousGravity, true);
- if (saveGravity != Gravity.NO_GRAVITY) {
- mPreviousGravity = saveGravity;
- }
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(/* expanding= */ true);
mTvPipBoundsState.setTvPipExpanded(true);
updatePinnedStackBounds();
}
@@ -635,11 +628,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onImeVisibilityChanged(), visible=%b, height=%d",
- TAG, imeVisible, imeHeight);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onImeVisibilityChanged(), visible=%b, height=%d",
+ TAG, imeVisible, imeHeight);
if (imeVisible == mTvPipBoundsState.isImeShowing()
&& (!imeVisible || imeHeight == mTvPipBoundsState.getImeHeight())) {
@@ -661,17 +652,13 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
private static TaskInfo getPinnedTaskInfo() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: getPinnedTaskInfo()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: getPinnedTaskInfo()", TAG);
try {
final TaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: taskInfo=%s", TAG, taskInfo);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: taskInfo=%s", TAG, taskInfo);
return taskInfo;
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -681,10 +668,8 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
private static void removeTask(int taskId) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: removeTask(), taskId=%d", TAG, taskId);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: removeTask(), taskId=%d", TAG, taskId);
try {
ActivityTaskManager.getService().removeTask(taskId);
} catch (Exception e) {
@@ -707,6 +692,90 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
}
+ private void executeAction(@TvPipAction.ActionType int actionType) {
+ switch (actionType) {
+ case TvPipAction.ACTION_FULLSCREEN:
+ movePipToFullscreen();
+ break;
+ case TvPipAction.ACTION_CLOSE:
+ closePip();
+ break;
+ case TvPipAction.ACTION_MOVE:
+ showPictureInPictureMenu(/* moveMenu= */ true);
+ break;
+ case TvPipAction.ACTION_CUSTOM_CLOSE:
+ customClosePip();
+ break;
+ case TvPipAction.ACTION_EXPAND_COLLAPSE:
+ togglePipExpansion();
+ break;
+ default:
+ // NOOP
+ break;
+ }
+ }
+
+ private class ActionBroadcastReceiver extends BroadcastReceiver {
+ private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
+
+ final IntentFilter mIntentFilter;
+
+ {
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_CLOSE_PIP);
+ mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
+ mIntentFilter.addAction(ACTION_MOVE_PIP);
+ mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
+ mIntentFilter.addAction(ACTION_TO_FULLSCREEN);
+ }
+
+ boolean mRegistered = false;
+
+ void register() {
+ if (mRegistered) return;
+
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
+ mMainHandler);
+ mRegistered = true;
+ }
+
+ void unregister() {
+ if (!mRegistered) return;
+
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: on(Broadcast)Receive(), action=%s", TAG, action);
+
+ if (ACTION_SHOW_PIP_MENU.equals(action)) {
+ showPictureInPictureMenu(/* moveMenu= */ false);
+ } else {
+ executeAction(getCorrespondingActionType(action));
+ }
+ }
+
+ @TvPipAction.ActionType
+ private int getCorrespondingActionType(String broadcast) {
+ if (ACTION_CLOSE_PIP.equals(broadcast)) {
+ return TvPipAction.ACTION_CLOSE;
+ } else if (ACTION_MOVE_PIP.equals(broadcast)) {
+ return TvPipAction.ACTION_MOVE;
+ } else if (ACTION_TOGGLE_EXPANDED_PIP.equals(broadcast)) {
+ return TvPipAction.ACTION_EXPAND_COLLAPSE;
+ } else if (ACTION_TO_FULLSCREEN.equals(broadcast)) {
+ return TvPipAction.ACTION_FULLSCREEN;
+ }
+
+ // Default: handle it like an action we don't know the content of.
+ return TvPipAction.ACTION_CUSTOM;
+ }
+ }
+
private class TvPipImpl implements Pip {
// Not used
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
new file mode 100644
index 000000000000..449a2bf09881
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
@@ -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.
+ */
+
+package com.android.wm.shell.pip.tv;
+
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.TvWindowMenuActionButton;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A TvPipAction for actions that the app provides via {@link
+ * android.app.PictureInPictureParams.Builder#setCloseAction(RemoteAction)} or {@link
+ * android.app.PictureInPictureParams.Builder#setActions(List)}.
+ */
+public class TvPipCustomAction extends TvPipAction {
+ private static final String TAG = TvPipCustomAction.class.getSimpleName();
+
+ private final RemoteAction mRemoteAction;
+
+ TvPipCustomAction(@ActionType int actionType, @NonNull RemoteAction remoteAction,
+ SystemActionsHandler systemActionsHandler) {
+ super(actionType, systemActionsHandler);
+ Objects.requireNonNull(remoteAction);
+ mRemoteAction = remoteAction;
+ }
+
+ void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler) {
+ if (button == null || mainHandler == null) return;
+ if (mRemoteAction.getContentDescription().length() > 0) {
+ button.setTextAndDescription(mRemoteAction.getContentDescription());
+ } else {
+ button.setTextAndDescription(mRemoteAction.getTitle());
+ }
+ button.setImageIconAsync(mRemoteAction.getIcon(), mainHandler);
+ button.setEnabled(isCloseAction() || mRemoteAction.isEnabled());
+ }
+
+ PendingIntent getPendingIntent() {
+ return mRemoteAction.getActionIntent();
+ }
+
+ void executeAction() {
+ super.executeAction();
+ try {
+ mRemoteAction.getActionIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Failed to send action, %s", TAG, e);
+ }
+ }
+
+ @Override
+ Notification.Action toNotificationAction(Context context) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(
+ mRemoteAction.getIcon(),
+ mRemoteAction.getTitle(),
+ mRemoteAction.getActionIntent());
+ Bundle extras = new Bundle();
+ extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
+ mRemoteAction.getContentDescription());
+ builder.addExtras(extras);
+
+ builder.setSemanticAction(isCloseAction()
+ ? SEMANTIC_ACTION_DELETE : SEMANTIC_ACTION_NONE);
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
index 1e54436ebce9..a94bd6ec1040 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
@@ -284,8 +284,10 @@ class TvPipKeepClearAlgorithm() {
): Rect? {
val movementBounds = transformedMovementBounds
val candidateEdgeRects = mutableListOf<Rect>()
+ val maxRestrictedXDistanceFraction =
+ if (isPipAnchoredToCorner()) maxRestrictedDistanceFraction else 0.0
val minRestrictedLeft =
- pipAnchorBounds.right - screenSize.width * maxRestrictedDistanceFraction
+ pipAnchorBounds.right - screenSize.width * maxRestrictedXDistanceFraction
candidateEdgeRects.add(
movementBounds.offsetCopy(movementBounds.width() + pipAreaPadding, 0)
@@ -296,7 +298,6 @@ class TvPipKeepClearAlgorithm() {
// throw out edges that are too close to the left screen edge to fit the PiP
val minLeft = movementBounds.left + pipAnchorBounds.width()
candidateEdgeRects.retainAll { it.left - pipAreaPadding > minLeft }
- candidateEdgeRects.sortBy { -it.left }
val maxRestrictedDY = (screenSize.height * maxRestrictedDistanceFraction).roundToInt()
@@ -335,8 +336,7 @@ class TvPipKeepClearAlgorithm() {
}
}
- candidateBounds.sortBy { candidateCost(it, pipAnchorBounds) }
- return candidateBounds.firstOrNull()
+ return candidateBounds.minByOrNull { candidateCost(it, pipAnchorBounds) }
}
private fun getNearbyStashedPosition(bounds: Rect, keepClearAreas: Set<Rect>): Rect {
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 4ce45e142c64..8895fcab0c3d 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
@@ -41,56 +41,44 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
/**
* Manages the visibility of the PiP Menu as user interacts with PiP.
*/
public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener {
private static final String TAG = "TvPipMenuController";
- private static final boolean DEBUG = TvPipController.DEBUG;
private static final String BACKGROUND_WINDOW_TITLE = "PipBackgroundView";
private final Context mContext;
private final SystemWindows mSystemWindows;
private final TvPipBoundsState mTvPipBoundsState;
private final Handler mMainHandler;
- private final int mPipMenuBorderWidth;
- private final int mPipEduTextShowDurationMs;
- private final int mPipEduTextHeight;
+ private TvPipActionsProvider mTvPipActionsProvider;
private Delegate mDelegate;
private SurfaceControl mLeash;
private TvPipMenuView mPipMenuView;
private View mPipBackgroundView;
+ private boolean mMenuIsOpen;
// User can actively move the PiP via the DPAD.
private boolean mInMoveMode;
// Used when only showing the move menu since we want to close the menu completely when
// exiting the move menu instead of showing the regular button menu.
private boolean mCloseAfterExitMoveMenu;
- private final List<RemoteAction> mMediaActions = new ArrayList<>();
- private final List<RemoteAction> mAppActions = new ArrayList<>();
- private RemoteAction mCloseAction;
-
private SyncRtSurfaceTransactionApplier mApplier;
private SyncRtSurfaceTransactionApplier mBackgroundApplier;
RectF mTmpSourceRectF = new RectF();
RectF mTmpDestinationRectF = new RectF();
Matrix mMoveTransform = new Matrix();
- private final Runnable mCloseEduTextRunnable = this::closeEduText;
-
public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState,
- SystemWindows systemWindows, PipMediaController pipMediaController,
- Handler mainHandler) {
+ SystemWindows systemWindows, Handler mainHandler) {
mContext = context;
mTvPipBoundsState = tvPipBoundsState;
mSystemWindows = systemWindows;
@@ -107,22 +95,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */,
mainHandler, Context.RECEIVER_EXPORTED);
-
- pipMediaController.addActionListener(this::onMediaActionsChanged);
-
- mPipEduTextShowDurationMs = context.getResources()
- .getInteger(R.integer.pip_edu_text_show_duration_ms);
- mPipEduTextHeight = context.getResources()
- .getDimensionPixelSize(R.dimen.pip_menu_edu_text_view_height);
- mPipMenuBorderWidth = context.getResources()
- .getDimensionPixelSize(R.dimen.pip_menu_border_width);
}
void setDelegate(Delegate delegate) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setDelegate(), delegate=%s", TAG, delegate);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: setDelegate(), delegate=%s", TAG, delegate);
if (mDelegate != null) {
throw new IllegalStateException(
"The delegate has already been set and should not change.");
@@ -134,6 +111,10 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mDelegate = delegate;
}
+ void setTvPipActionsProvider(TvPipActionsProvider tvPipActionsProvider) {
+ mTvPipActionsProvider = tvPipActionsProvider;
+ }
+
@Override
public void attach(SurfaceControl leash) {
if (mDelegate == null) {
@@ -145,10 +126,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
private void attachPipMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: attachPipMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: attachPipMenu()", TAG);
if (mPipMenuView != null) {
detachPipMenu();
@@ -157,18 +136,24 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
attachPipBackgroundView();
attachPipMenuView();
- mTvPipBoundsState.setPipMenuPermanentDecorInsets(Insets.of(-mPipMenuBorderWidth,
- -mPipMenuBorderWidth, -mPipMenuBorderWidth, -mPipMenuBorderWidth));
- mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.of(0, 0, 0, -mPipEduTextHeight));
- mMainHandler.postDelayed(mCloseEduTextRunnable, mPipEduTextShowDurationMs);
+ int pipEduTextHeight = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.pip_menu_edu_text_view_height);
+ int pipMenuBorderWidth = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.pip_menu_border_width);
+ mTvPipBoundsState.setPipMenuPermanentDecorInsets(Insets.of(-pipMenuBorderWidth,
+ -pipMenuBorderWidth, -pipMenuBorderWidth, -pipMenuBorderWidth));
+ mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.of(0, 0, 0, -pipEduTextHeight));
}
private void attachPipMenuView() {
- mPipMenuView = new TvPipMenuView(mContext);
- mPipMenuView.setListener(this);
+ if (mTvPipActionsProvider == null) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Actions provider is not set", TAG);
+ return;
+ }
+ mPipMenuView = new TvPipMenuView(mContext, mMainHandler, this, mTvPipActionsProvider);
setUpViewSurfaceZOrder(mPipMenuView, 1);
addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
- maybeUpdateMenuViewActions();
}
private void attachPipBackgroundView() {
@@ -180,45 +165,50 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private void setUpViewSurfaceZOrder(View v, int zOrderRelativeToPip) {
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- v.getViewRootImpl().addSurfaceChangedCallback(
- new PipMenuSurfaceChangedCallback(v, zOrderRelativeToPip));
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- }
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewRootImpl().addSurfaceChangedCallback(
+ new PipMenuSurfaceChangedCallback(v, zOrderRelativeToPip));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
});
}
private void addPipMenuViewToSystemWindows(View v, String title) {
- mSystemWindows.addView(v, getPipMenuLayoutParams(title, 0 /* width */, 0 /* height */),
- 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
- }
-
- void notifyPipAnimating(boolean animating) {
- mPipMenuView.setEduTextActive(!animating);
- if (!animating) {
- mPipMenuView.onPipTransitionFinished();
- }
+ mSystemWindows.addView(v, getPipMenuLayoutParams(mContext, title, 0 /* width */,
+ 0 /* height */), 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
+ }
+
+ void onPipTransitionFinished(boolean enterTransition) {
+ // There is a race between when this is called and when the last frame of the pip transition
+ // is drawn. To ensure that view updates are applied only when the animation has fully drawn
+ // and the menu view has been fully remeasured and relaid out, we add a small delay here by
+ // posting on the handler.
+ mMainHandler.post(() -> {
+ if (mPipMenuView != null) {
+ mPipMenuView.onPipTransitionFinished(enterTransition);
+ }
+ });
}
- void showMovementMenuOnly() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showMovementMenuOnly()", TAG);
- }
+ void showMovementMenu() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showMovementMenuOnly()", TAG);
setInMoveMode(true);
- mCloseAfterExitMoveMenu = true;
- showMenuInternal();
+ if (mMenuIsOpen) {
+ mPipMenuView.showMoveMenu(mTvPipBoundsState.getTvPipGravity());
+ } else {
+ mCloseAfterExitMoveMenu = true;
+ showMenuInternal();
+ }
}
@Override
public void showMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
setInMoveMode(false);
mCloseAfterExitMoveMenu = false;
showMenuInternal();
@@ -228,46 +218,27 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (mPipMenuView == null) {
return;
}
- maybeCloseEduText();
- maybeUpdateMenuViewActions();
- updateExpansionState();
+ mMenuIsOpen = true;
grantPipMenuFocus(true);
if (mInMoveMode) {
- mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
+ mPipMenuView.showMoveMenu(mTvPipBoundsState.getTvPipGravity());
} else {
- mPipMenuView.showButtonsMenu();
+ mPipMenuView.showButtonsMenu(/* exitingMoveMode= */ false);
}
mPipMenuView.updateBounds(mTvPipBoundsState.getBounds());
}
- void onPipTransitionStarted(Rect finishBounds) {
+ void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
if (mPipMenuView != null) {
- mPipMenuView.onPipTransitionStarted(finishBounds);
+ mPipMenuView.onPipTransitionToTargetBoundsStarted(targetBounds);
}
}
- private void maybeCloseEduText() {
- if (mMainHandler.hasCallbacks(mCloseEduTextRunnable)) {
- mMainHandler.removeCallbacks(mCloseEduTextRunnable);
- mCloseEduTextRunnable.run();
- }
- }
-
- private void closeEduText() {
- mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.NONE);
- mPipMenuView.hideEduText();
- mDelegate.closeEduText();
- }
-
void updateGravity(int gravity) {
- mPipMenuView.showMovementHints(gravity);
- }
-
- void updateExpansionState() {
- mPipMenuView.setExpandedModeEnabled(mTvPipBoundsState.isTvExpandedPipSupported()
- && mTvPipBoundsState.getDesiredTvExpandedAspectRatio() != 0);
- mPipMenuView.setIsExpanded(mTvPipBoundsState.isTvPipExpanded());
+ if (mInMoveMode) {
+ mPipMenuView.showMovementHints(gravity);
+ }
}
private Rect calculateMenuSurfaceBounds(Rect pipBounds) {
@@ -275,15 +246,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
void closeMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: closeMenu()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closeMenu()", TAG);
if (mPipMenuView == null) {
return;
}
+ mMenuIsOpen = false;
mPipMenuView.hideAllUserControls();
grantPipMenuFocus(false);
mDelegate.onMenuClosed();
@@ -297,7 +267,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (mInMoveMode == moveMode) {
return;
}
-
mInMoveMode = moveMode;
if (mDelegate != null) {
mDelegate.onInMoveModeChanged();
@@ -305,32 +274,19 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
- public void onEnterMoveMode() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onEnterMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
- mCloseAfterExitMoveMenu);
- }
- setInMoveMode(true);
- mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
- }
-
- @Override
public boolean onExitMoveMode() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExitMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
- mCloseAfterExitMoveMenu);
- }
- if (mCloseAfterExitMoveMenu) {
- setInMoveMode(false);
- mCloseAfterExitMoveMenu = false;
- closeMenu();
- return true;
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onExitMoveMode - %b, close when exiting move menu: %b",
+ TAG, mInMoveMode, mCloseAfterExitMoveMenu);
+
if (mInMoveMode) {
setInMoveMode(false);
- mPipMenuView.showButtonsMenu();
+ if (mCloseAfterExitMoveMenu) {
+ mCloseAfterExitMoveMenu = false;
+ closeMenu();
+ } else {
+ mPipMenuView.showButtonsMenu(/* exitingMoveMode= */ true);
+ }
return true;
}
return false;
@@ -338,10 +294,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public boolean onPipMovement(int keycode) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipMovement - %b", TAG, mInMoveMode);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipMovement - %b", TAG, mInMoveMode);
if (mInMoveMode) {
mDelegate.movePip(keycode);
}
@@ -351,62 +305,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void detach() {
closeMenu();
- mMainHandler.removeCallbacks(mCloseEduTextRunnable);
detachPipMenu();
mLeash = null;
}
@Override
public void setAppActions(List<RemoteAction> actions, RemoteAction closeAction) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setAppActions()", TAG);
- }
- updateAdditionalActionsList(mAppActions, actions, closeAction);
- }
-
- private void onMediaActionsChanged(List<RemoteAction> actions) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onMediaActionsChanged()", TAG);
- }
-
- // Hide disabled actions.
- List<RemoteAction> enabledActions = new ArrayList<>();
- for (RemoteAction remoteAction : actions) {
- if (remoteAction.isEnabled()) {
- enabledActions.add(remoteAction);
- }
- }
- updateAdditionalActionsList(mMediaActions, enabledActions, mCloseAction);
- }
-
- private void updateAdditionalActionsList(List<RemoteAction> destination,
- @Nullable List<RemoteAction> source, RemoteAction closeAction) {
- final int number = source != null ? source.size() : 0;
- if (number == 0 && destination.isEmpty() && Objects.equals(closeAction, mCloseAction)) {
- // Nothing changed.
- return;
- }
-
- mCloseAction = closeAction;
-
- destination.clear();
- if (number > 0) {
- destination.addAll(source);
- }
- maybeUpdateMenuViewActions();
- }
-
- private void maybeUpdateMenuViewActions() {
- if (mPipMenuView == null) {
- return;
- }
- if (!mAppActions.isEmpty()) {
- mPipMenuView.setAdditionalActions(mAppActions, mCloseAction, mMainHandler);
- } else {
- mPipMenuView.setAdditionalActions(mMediaActions, mCloseAction, mMainHandler);
- }
+ // NOOP - handled via the TvPipActionsProvider
}
@Override
@@ -421,10 +326,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
public void resizePipMenu(@Nullable SurfaceControl pipLeash,
@Nullable SurfaceControl.Transaction t,
Rect destinationBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: resizePipMenu: %s", TAG, destinationBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: resizePipMenu: %s", TAG, destinationBounds.toShortString());
if (destinationBounds.isEmpty()) {
return;
}
@@ -438,14 +341,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView);
final SyncRtSurfaceTransactionApplier.SurfaceParams frontParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(frontSurface)
- .withWindowCrop(menuBounds)
- .build();
+ .withWindowCrop(menuBounds)
+ .build();
final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView);
final SyncRtSurfaceTransactionApplier.SurfaceParams backParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(backSurface)
- .withWindowCrop(menuBounds)
- .build();
+ .withWindowCrop(menuBounds)
+ .build();
// TODO(b/226580399): switch to using SurfaceSyncer (see b/200284684) to synchronize the
// animations of the pip surface with the content of the front and back menu surfaces
@@ -468,13 +371,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
Rect pipDestBounds) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipMenu: %s", TAG, pipDestBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: movePipMenu: %s", TAG, pipDestBounds.toShortString());
if (pipDestBounds.isEmpty()) {
- if (transaction == null && DEBUG) {
+ if (transaction == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: no transaction given", TAG);
}
@@ -490,16 +391,12 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
// resizing and the PiP menu is also resized. We then want to do a scale from the current
// new menu bounds.
if (pipLeash != null && transaction != null) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: tmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: tmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG);
mPipMenuView.getBoundsOnScreen(tmpSourceBounds);
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: tmpSourceBounds based on menu width and height", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: tmpSourceBounds based on menu width and height", TAG);
tmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height());
}
@@ -525,8 +422,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (pipLeash != null && transaction != null) {
final SyncRtSurfaceTransactionApplier.SurfaceParams pipParams =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
- .withMergeTransaction(transaction)
- .build();
+ .withMergeTransaction(transaction)
+ .build();
mApplier.scheduleApply(frontParams, pipParams);
} else {
mApplier.scheduleApply(frontParams);
@@ -568,15 +465,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void updateMenuBounds(Rect destinationBounds) {
final Rect menuBounds = calculateMenuSurfaceBounds(destinationBounds);
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: updateMenuBounds: %s", TAG, menuBounds.toShortString());
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateMenuBounds: %s", TAG, menuBounds.toShortString());
mSystemWindows.updateViewLayout(mPipBackgroundView,
- getPipMenuLayoutParams(BACKGROUND_WINDOW_TITLE, menuBounds.width(),
+ getPipMenuLayoutParams(mContext, BACKGROUND_WINDOW_TITLE, menuBounds.width(),
menuBounds.height()));
mSystemWindows.updateViewLayout(mPipMenuView,
- getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(),
+ getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, menuBounds.width(),
menuBounds.height()));
if (mPipMenuView != null) {
@@ -597,43 +492,24 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
- public void onCloseButtonClick() {
- mDelegate.closePip();
- }
-
- @Override
- public void onFullscreenButtonClick() {
- mDelegate.movePipToFullscreen();
- }
-
- @Override
- public void onToggleExpandedMode() {
- mDelegate.togglePipExpansion();
+ public void onCloseEduText() {
+ mTvPipBoundsState.setPipMenuTemporaryDecorInsets(Insets.NONE);
+ mDelegate.closeEduText();
}
interface Delegate {
- void movePipToFullscreen();
-
void movePip(int keycode);
void onInMoveModeChanged();
- int getPipGravity();
-
- void togglePipExpansion();
-
void onMenuClosed();
void closeEduText();
-
- void closePip();
}
private void grantPipMenuFocus(boolean grantFocus) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: grantWindowFocus(%b)", TAG, grantFocus);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: grantWindowFocus(%b)", TAG, grantFocus);
try {
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
new file mode 100644
index 000000000000..6eef22562caa
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
@@ -0,0 +1,280 @@
+/*
+ * 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.pip.tv;
+
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.CENTER;
+import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+
+import java.util.Arrays;
+
+/**
+ * The edu text drawer shows the user a hint for how to access the Picture-in-Picture menu.
+ * It displays a text in a drawer below the Picture-in-Picture window. The drawer has the same
+ * width as the Picture-in-Picture window. Depending on the Picture-in-Picture mode, there might
+ * not be enough space to fit the whole educational text in the available space. In such cases we
+ * apply a marquee animation to the TextView inside the drawer.
+ *
+ * The drawer is shown temporarily giving the user enough time to read it, after which it slides
+ * shut. We show the text for a duration calculated based on whether the text is marqueed or not.
+ */
+class TvPipMenuEduTextDrawer extends FrameLayout {
+ private static final String TAG = "TvPipMenuEduTextDrawer";
+
+ private static final float MARQUEE_DP_PER_SECOND = 30; // Copy of TextView.MARQUEE_DP_PER_SECOND
+ private static final int MARQUEE_RESTART_DELAY = 1200; // Copy of TextView.MARQUEE_DELAY
+ private final float mMarqueeAnimSpeed; // pixels per ms
+
+ private final Runnable mCloseDrawerRunnable = this::closeDrawer;
+ private final Runnable mStartScrollEduTextRunnable = this::startScrollEduText;
+
+ private final Handler mMainHandler;
+ private final Listener mListener;
+ private final TextView mEduTextView;
+
+ TvPipMenuEduTextDrawer(@NonNull Context context, Handler mainHandler, Listener listener) {
+ super(context, null, 0, 0);
+
+ mListener = listener;
+ mMainHandler = mainHandler;
+
+ // Taken from TextView.Marquee calculation
+ mMarqueeAnimSpeed =
+ (MARQUEE_DP_PER_SECOND * context.getResources().getDisplayMetrics().density) / 1000f;
+
+ mEduTextView = new TextView(mContext);
+ setupDrawer();
+ }
+
+ private void setupDrawer() {
+ final int eduTextHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.pip_menu_edu_text_view_height);
+ final int marqueeRepeatLimit = mContext.getResources()
+ .getInteger(R.integer.pip_edu_text_scroll_times);
+
+ mEduTextView.setLayoutParams(
+ new LayoutParams(MATCH_PARENT, eduTextHeight, BOTTOM | CENTER));
+ mEduTextView.setGravity(CENTER);
+ mEduTextView.setClickable(false);
+ mEduTextView.setText(createEduTextString());
+ mEduTextView.setSingleLine();
+ mEduTextView.setTextAppearance(R.style.TvPipEduText);
+ mEduTextView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
+ mEduTextView.setMarqueeRepeatLimit(marqueeRepeatLimit);
+ mEduTextView.setHorizontallyScrolling(true);
+ mEduTextView.setHorizontalFadingEdgeEnabled(true);
+ mEduTextView.setSelected(false);
+ addView(mEduTextView);
+
+ setLayoutParams(new LayoutParams(MATCH_PARENT, eduTextHeight, CENTER));
+ setClipChildren(true);
+ }
+
+ /**
+ * Initializes the edu text. Should only be called once when the PiP is entered
+ */
+ void init() {
+ ProtoLog.i(WM_SHELL_PICTURE_IN_PICTURE, "%s: init()", TAG);
+ scheduleLifecycleEvents();
+ }
+
+ private void scheduleLifecycleEvents() {
+ final int startScrollDelay = mContext.getResources().getInteger(
+ R.integer.pip_edu_text_start_scroll_delay);
+ if (isEduTextMarqueed()) {
+ mMainHandler.postDelayed(mStartScrollEduTextRunnable, startScrollDelay);
+ }
+ mMainHandler.postDelayed(mCloseDrawerRunnable, startScrollDelay + getEduTextShowDuration());
+ mEduTextView.getViewTreeObserver().addOnWindowAttachListener(
+ new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ }
+
+ @Override
+ public void onWindowDetached() {
+ mEduTextView.getViewTreeObserver().removeOnWindowAttachListener(this);
+ mMainHandler.removeCallbacks(mStartScrollEduTextRunnable);
+ mMainHandler.removeCallbacks(mCloseDrawerRunnable);
+ }
+ });
+ }
+
+ private int getEduTextShowDuration() {
+ int eduTextShowDuration;
+ if (isEduTextMarqueed()) {
+ // Calculate the time it takes to fully scroll the text once: time = distance / speed
+ final float singleMarqueeDuration =
+ getMarqueeAnimEduTextLineWidth() / mMarqueeAnimSpeed;
+ // The TextView adds a delay between each marquee repetition. Take that into account
+ final float durationFromStartToStart = singleMarqueeDuration + MARQUEE_RESTART_DELAY;
+ // Finally, multiply by the number of times we repeat the marquee animation
+ eduTextShowDuration =
+ (int) durationFromStartToStart * mEduTextView.getMarqueeRepeatLimit();
+ } else {
+ eduTextShowDuration = mContext.getResources()
+ .getInteger(R.integer.pip_edu_text_non_scroll_show_duration);
+ }
+
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: getEduTextShowDuration(), showDuration=%d",
+ TAG, eduTextShowDuration);
+ return eduTextShowDuration;
+ }
+
+ /**
+ * Returns true if the edu text width is bigger than the width of the text view, which indicates
+ * that the edu text will be marqueed
+ */
+ private boolean isEduTextMarqueed() {
+ final int availableWidth = (int) mEduTextView.getWidth()
+ - mEduTextView.getCompoundPaddingLeft()
+ - mEduTextView.getCompoundPaddingRight();
+ return availableWidth < getEduTextWidth();
+ }
+
+ /**
+ * Returns the width of a single marquee repetition of the edu text in pixels.
+ * This is the width from the start of the edu text to the start of the next edu
+ * text when it is marqueed.
+ *
+ * This is calculated based on the TextView.Marquee#start calculations
+ */
+ private float getMarqueeAnimEduTextLineWidth() {
+ // When the TextView has a marquee animation, it puts a gap between the text end and the
+ // start of the next edu text repetition. The space is equal to a third of the TextView
+ // width
+ final float gap = mEduTextView.getWidth() / 3.0f;
+ return getEduTextWidth() + gap;
+ }
+
+ private void startScrollEduText() {
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: startScrollEduText(), repeat=%d",
+ TAG, mEduTextView.getMarqueeRepeatLimit());
+ mEduTextView.setSelected(true);
+ }
+
+ /**
+ * Returns the width of the edu text irrespective of the TextView width
+ */
+ private int getEduTextWidth() {
+ return (int) mEduTextView.getLayout().getLineWidth(0);
+ }
+
+ /**
+ * Closes the edu text drawer if it hasn't been closed yet
+ */
+ void closeIfNeeded() {
+ if (mMainHandler.hasCallbacks(mCloseDrawerRunnable)) {
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: close(), closing the edu text drawer because of user action", TAG);
+ mMainHandler.removeCallbacks(mCloseDrawerRunnable);
+ mCloseDrawerRunnable.run();
+ } else {
+ // Do nothing, the drawer has already been closed
+ }
+ }
+
+ private void closeDrawer() {
+ ProtoLog.i(WM_SHELL_PICTURE_IN_PICTURE, "%s: closeDrawer()", TAG);
+ final int eduTextFadeExitAnimationDuration = mContext.getResources().getInteger(
+ R.integer.pip_edu_text_view_exit_animation_duration);
+ final int eduTextSlideExitAnimationDuration = mContext.getResources().getInteger(
+ R.integer.pip_edu_text_window_exit_animation_duration);
+
+ // Start fading out the edu text
+ mEduTextView.animate()
+ .alpha(0f)
+ .setInterpolator(TvPipInterpolators.EXIT)
+ .setDuration(eduTextFadeExitAnimationDuration)
+ .start();
+
+ // Start animation to close the drawer by animating its height to 0
+ final ValueAnimator heightAnimation = ValueAnimator.ofInt(getHeight(), 0);
+ heightAnimation.setDuration(eduTextSlideExitAnimationDuration);
+ heightAnimation.setInterpolator(TvPipInterpolators.BROWSE);
+ heightAnimation.addUpdateListener(animator -> {
+ final ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = (int) animator.getAnimatedValue();
+ setLayoutParams(params);
+ if (params.height == 0) {
+ setVisibility(GONE);
+ }
+ });
+ heightAnimation.start();
+
+ mListener.onCloseEduText();
+ }
+
+ /**
+ * Creates the educational text that will be displayed to the user. Here we replace the
+ * HOME annotation in the String with an icon
+ */
+ private CharSequence createEduTextString() {
+ final SpannedString eduText = (SpannedString) getResources().getText(R.string.pip_edu_text);
+ final SpannableString spannableString = new SpannableString(eduText);
+ Arrays.stream(eduText.getSpans(0, eduText.length(), Annotation.class)).findFirst()
+ .ifPresent(annotation -> {
+ final Drawable icon =
+ getResources().getDrawable(R.drawable.home_icon, mContext.getTheme());
+ if (icon != null) {
+ icon.mutate();
+ icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+ spannableString.setSpan(new CenteredImageSpan(icon),
+ eduText.getSpanStart(annotation),
+ eduText.getSpanEnd(annotation),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ });
+
+ return spannableString;
+ }
+
+ /**
+ * A listener for edu text drawer event states.
+ */
+ interface Listener {
+ /**
+ * The edu text closing impacts the size of the Picture-in-Picture window and influences
+ * how it is positioned on the screen.
+ */
+ void onCloseEduText();
+ }
+
+}
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 320c05c4a415..56c602a1d4f3 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
@@ -25,205 +25,135 @@ import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
import static android.view.KeyEvent.KEYCODE_ENTER;
-import android.animation.ValueAnimator;
-import android.app.PendingIntent;
-import android.app.RemoteAction;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE;
+
import android.content.Context;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.text.Annotation;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.SpannedString;
-import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewRootImpl;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.TvWindowMenuActionButton;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
- * A View that represents Pip Menu on TV. It's responsible for displaying 3 ever-present Pip Menu
- * actions: Fullscreen, Move and Close, but could also display "additional" actions, that may be set
- * via a {@link #setAdditionalActions(List, Handler)} call.
+ * A View that represents Pip Menu on TV. It's responsible for displaying the Pip menu actions from
+ * the TvPipActionsProvider as well as the buttons for manually moving the PiP.
*/
-public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
+public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.Listener {
private static final String TAG = "TvPipMenuView";
- private static final boolean DEBUG = TvPipController.DEBUG;
- private static final int FIRST_CUSTOM_ACTION_POSITION = 3;
+ private final TvPipMenuView.Listener mListener;
- @Nullable
- private Listener mListener;
+ private final TvPipActionsProvider mTvPipActionsProvider;
+
+ private final RecyclerView mActionButtonsRecyclerView;
+ private final LinearLayoutManager mButtonLayoutManager;
+ private final RecyclerViewAdapter mRecyclerViewAdapter;
- private final LinearLayout mActionButtonsContainer;
- private final View mMenuFrameView;
- private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
private final View mPipFrameView;
+ private final View mMenuFrameView;
private final View mPipView;
- private final TextView mEduTextView;
- private final View mEduTextContainerView;
+
+ private final View mPipBackground;
+ private final View mDimLayer;
+
+ private final TvPipMenuEduTextDrawer mEduTextDrawer;
+
private final int mPipMenuOuterSpace;
private final int mPipMenuBorderWidth;
- private final int mEduTextFadeExitAnimationDurationMs;
- private final int mEduTextSlideExitAnimationDurationMs;
- private int mEduTextHeight;
+
+ private final int mPipMenuFadeAnimationDuration;
+ private final int mResizeAnimationDuration;
private final ImageView mArrowUp;
private final ImageView mArrowRight;
private final ImageView mArrowDown;
private final ImageView mArrowLeft;
-
- private final ScrollView mScrollView;
- private final HorizontalScrollView mHorizontalScrollView;
- private View mFocusedButton;
+ private final TvWindowMenuActionButton mA11yDoneButton;
private Rect mCurrentPipBounds;
private boolean mMoveMenuIsVisible;
private boolean mButtonMenuIsVisible;
-
- private final TvPipMenuActionButton mExpandButton;
- private final TvPipMenuActionButton mCloseButton;
-
private boolean mSwitchingOrientation;
- private final int mPipMenuFadeAnimationDuration;
- private final int mResizeAnimationDuration;
-
- public TvPipMenuView(@NonNull Context context) {
- this(context, null);
- }
-
- public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ private final AccessibilityManager mA11yManager;
+ private final Handler mMainHandler;
+ public TvPipMenuView(@NonNull Context context, @NonNull Handler mainHandler,
+ @NonNull Listener listener, TvPipActionsProvider tvPipActionsProvider) {
+ super(context, null, 0, 0);
inflate(context, R.layout.tv_pip_menu, this);
- mActionButtonsContainer = findViewById(R.id.tv_pip_menu_action_buttons);
- mActionButtonsContainer.findViewById(R.id.tv_pip_menu_fullscreen_button)
- .setOnClickListener(this);
+ mMainHandler = mainHandler;
+ mListener = listener;
+ mA11yManager = context.getSystemService(AccessibilityManager.class);
- mCloseButton = mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button);
- mCloseButton.setOnClickListener(this);
- mCloseButton.setIsCustomCloseAction(true);
+ mActionButtonsRecyclerView = findViewById(R.id.tv_pip_menu_action_buttons);
+ mButtonLayoutManager = new LinearLayoutManager(mContext);
+ mActionButtonsRecyclerView.setLayoutManager(mButtonLayoutManager);
+ mActionButtonsRecyclerView.setPreserveFocusAfterLayout(true);
- mActionButtonsContainer.findViewById(R.id.tv_pip_menu_move_button)
- .setOnClickListener(this);
- mExpandButton = findViewById(R.id.tv_pip_menu_expand_button);
- mExpandButton.setOnClickListener(this);
+ mTvPipActionsProvider = tvPipActionsProvider;
+ mRecyclerViewAdapter = new RecyclerViewAdapter(tvPipActionsProvider.getActionsList());
+ mActionButtonsRecyclerView.setAdapter(mRecyclerViewAdapter);
- mScrollView = findViewById(R.id.tv_pip_menu_scroll);
- mHorizontalScrollView = findViewById(R.id.tv_pip_menu_horizontal_scroll);
+ tvPipActionsProvider.addListener(this);
mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
mPipFrameView = findViewById(R.id.tv_pip_border);
mPipView = findViewById(R.id.tv_pip);
+ mPipBackground = findViewById(R.id.tv_pip_menu_background);
+ mDimLayer = findViewById(R.id.tv_pip_menu_dim_layer);
+
mArrowUp = findViewById(R.id.tv_pip_menu_arrow_up);
mArrowRight = findViewById(R.id.tv_pip_menu_arrow_right);
mArrowDown = findViewById(R.id.tv_pip_menu_arrow_down);
mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left);
-
- mEduTextView = findViewById(R.id.tv_pip_menu_edu_text);
- mEduTextContainerView = findViewById(R.id.tv_pip_menu_edu_text_container);
+ mA11yDoneButton = findViewById(R.id.tv_pip_menu_done_button);
mResizeAnimationDuration = context.getResources().getInteger(
R.integer.config_pipResizeAnimationDuration);
mPipMenuFadeAnimationDuration = context.getResources()
- .getInteger(R.integer.pip_menu_fade_animation_duration);
+ .getInteger(R.integer.tv_window_menu_fade_animation_duration);
mPipMenuOuterSpace = context.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
mPipMenuBorderWidth = context.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_border_width);
- mEduTextHeight = context.getResources()
- .getDimensionPixelSize(R.dimen.pip_menu_edu_text_view_height);
- mEduTextFadeExitAnimationDurationMs = context.getResources()
- .getInteger(R.integer.pip_edu_text_view_exit_animation_duration_ms);
- mEduTextSlideExitAnimationDurationMs = context.getResources()
- .getInteger(R.integer.pip_edu_text_window_exit_animation_duration_ms);
-
- initEduText();
- }
- void initEduText() {
- final SpannedString eduText = (SpannedString) getResources().getText(R.string.pip_edu_text);
- final SpannableString spannableString = new SpannableString(eduText);
- Arrays.stream(eduText.getSpans(0, eduText.length(), Annotation.class)).findFirst()
- .ifPresent(annotation -> {
- final Drawable icon =
- getResources().getDrawable(R.drawable.home_icon, mContext.getTheme());
- if (icon != null) {
- icon.mutate();
- icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
- spannableString.setSpan(new CenteredImageSpan(icon),
- eduText.getSpanStart(annotation),
- eduText.getSpanEnd(annotation),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- });
-
- mEduTextView.setText(spannableString);
- }
-
- void setEduTextActive(boolean active) {
- mEduTextView.setSelected(active);
+ mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, mListener);
+ ((FrameLayout) findViewById(R.id.tv_pip_menu_edu_text_drawer_placeholder))
+ .addView(mEduTextDrawer);
}
- void hideEduText() {
- final ValueAnimator heightAnimation = ValueAnimator.ofInt(mEduTextHeight, 0);
- heightAnimation.setDuration(mEduTextSlideExitAnimationDurationMs);
- heightAnimation.setInterpolator(TvPipInterpolators.BROWSE);
- heightAnimation.addUpdateListener(animator -> {
- mEduTextHeight = (int) animator.getAnimatedValue();
- });
- mEduTextView.animate()
- .alpha(0f)
- .setInterpolator(TvPipInterpolators.EXIT)
- .setDuration(mEduTextFadeExitAnimationDurationMs)
- .withEndAction(() -> {
- mEduTextContainerView.setVisibility(GONE);
- }).start();
- heightAnimation.start();
- }
+ void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
+ if (targetBounds == null) {
+ return;
+ }
- void onPipTransitionStarted(Rect finishBounds) {
// Fade out content by fading in view on top.
- if (mCurrentPipBounds != null && finishBounds != null) {
+ if (mCurrentPipBounds != null) {
boolean ratioChanged = PipUtils.aspectRatioChanged(
mCurrentPipBounds.width() / (float) mCurrentPipBounds.height(),
- finishBounds.width() / (float) finishBounds.height());
+ targetBounds.width() / (float) targetBounds.height());
if (ratioChanged) {
- mPipView.animate()
+ mPipBackground.animate()
.alpha(1f)
.setInterpolator(TvPipInterpolators.EXIT)
.setDuration(mResizeAnimationDuration / 2)
@@ -232,52 +162,54 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
}
// Update buttons.
- final boolean vertical = finishBounds.height() > finishBounds.width();
+ final boolean vertical = targetBounds.height() > targetBounds.width();
final boolean orientationChanged =
- vertical != (mActionButtonsContainer.getOrientation() == LinearLayout.VERTICAL);
+ vertical != (mButtonLayoutManager.getOrientation() == LinearLayoutManager.VERTICAL);
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onPipTransitionStarted(), orientation changed %b", TAG, orientationChanged);
+ "%s: onPipTransitionToTargetBoundsStarted(), orientation changed %b",
+ TAG, orientationChanged);
if (!orientationChanged) {
return;
}
if (mButtonMenuIsVisible) {
mSwitchingOrientation = true;
- mActionButtonsContainer.animate()
+ mActionButtonsRecyclerView.animate()
.alpha(0)
.setInterpolator(TvPipInterpolators.EXIT)
.setDuration(mResizeAnimationDuration / 2)
.withEndAction(() -> {
- changeButtonScrollOrientation(finishBounds);
- updateButtonGravity(finishBounds);
+ mButtonLayoutManager.setOrientation(vertical
+ ? LinearLayoutManager.VERTICAL : LinearLayoutManager.HORIZONTAL);
// Only make buttons visible again in onPipTransitionFinished to keep in
// sync with PiP content alpha animation.
});
} else {
- changeButtonScrollOrientation(finishBounds);
- updateButtonGravity(finishBounds);
+ mButtonLayoutManager.setOrientation(vertical
+ ? LinearLayoutManager.VERTICAL : LinearLayoutManager.HORIZONTAL);
}
}
- void onPipTransitionFinished() {
+ void onPipTransitionFinished(boolean enterTransition) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onPipTransitionFinished()", TAG);
- // Fade in content by fading out view on top.
- mPipView.animate()
+ // Fade in content by fading out view on top (faded out at every aspect ratio change).
+ mPipBackground.animate()
.alpha(0f)
.setDuration(mResizeAnimationDuration / 2)
.setInterpolator(TvPipInterpolators.ENTER)
.start();
- // Update buttons.
+ if (enterTransition) {
+ mEduTextDrawer.init();
+ }
+
if (mSwitchingOrientation) {
- mActionButtonsContainer.animate()
+ mActionButtonsRecyclerView.animate()
.alpha(1)
.setInterpolator(TvPipInterpolators.ENTER)
.setDuration(mResizeAnimationDuration / 2);
- } else {
- refocusPreviousButton();
}
mSwitchingOrientation = false;
}
@@ -290,111 +222,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
"%s: updateLayout, width: %s, height: %s", TAG, updatedBounds.width(),
updatedBounds.height());
mCurrentPipBounds = updatedBounds;
- if (!mSwitchingOrientation) {
- updateButtonGravity(mCurrentPipBounds);
- }
-
updatePipFrameBounds();
}
- private void changeButtonScrollOrientation(Rect bounds) {
- final boolean vertical = bounds.height() > bounds.width();
-
- final ViewGroup oldScrollView = vertical ? mHorizontalScrollView : mScrollView;
- final ViewGroup newScrollView = vertical ? mScrollView : mHorizontalScrollView;
-
- if (oldScrollView.getChildCount() == 1) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: orientation changed", TAG);
- oldScrollView.removeView(mActionButtonsContainer);
- oldScrollView.setVisibility(GONE);
- mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
- : LinearLayout.HORIZONTAL);
- newScrollView.addView(mActionButtonsContainer);
- newScrollView.setVisibility(VISIBLE);
- if (mFocusedButton != null) {
- mFocusedButton.requestFocus();
- }
- }
- }
-
- /**
- * Change button gravity based on new dimensions
- */
- private void updateButtonGravity(Rect bounds) {
- final boolean vertical = bounds.height() > bounds.width();
- // Use Math.max since the possible orientation change might not have been applied yet.
- final int buttonsSize = Math.max(mActionButtonsContainer.getHeight(),
- mActionButtonsContainer.getWidth());
-
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: buttons container width: %s, height: %s", TAG,
- mActionButtonsContainer.getWidth(), mActionButtonsContainer.getHeight());
-
- final boolean buttonsFit =
- vertical ? buttonsSize < bounds.height()
- : buttonsSize < bounds.width();
- final int buttonGravity = buttonsFit ? Gravity.CENTER
- : (vertical ? Gravity.CENTER_HORIZONTAL : Gravity.CENTER_VERTICAL);
-
- final LayoutParams params = (LayoutParams) mActionButtonsContainer.getLayoutParams();
- params.gravity = buttonGravity;
- mActionButtonsContainer.setLayoutParams(params);
-
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: vertical: %b, buttonsFit: %b, gravity: %s", TAG, vertical, buttonsFit,
- Gravity.toString(buttonGravity));
- }
-
- private void refocusPreviousButton() {
- if (mMoveMenuIsVisible || mCurrentPipBounds == null || mFocusedButton == null) {
- return;
- }
- final boolean vertical = mCurrentPipBounds.height() > mCurrentPipBounds.width();
-
- if (!mFocusedButton.hasFocus()) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: request focus from: %s", TAG, mFocusedButton);
- mFocusedButton.requestFocus();
- } else {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: already focused: %s", TAG, mFocusedButton);
- }
-
- // Do we need to scroll?
- final Rect buttonBounds = new Rect();
- final Rect scrollBounds = new Rect();
- if (vertical) {
- mScrollView.getDrawingRect(scrollBounds);
- } else {
- mHorizontalScrollView.getDrawingRect(scrollBounds);
- }
- mFocusedButton.getHitRect(buttonBounds);
-
- if (scrollBounds.contains(buttonBounds)) {
- // Button is already completely visible, don't scroll
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: not scrolling", TAG);
- return;
- }
-
- // Scrolling so the button is visible to the user.
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: scrolling to focused button", TAG);
-
- if (vertical) {
- mScrollView.smoothScrollTo((int) mFocusedButton.getX(),
- (int) mFocusedButton.getY());
- } else {
- mHorizontalScrollView.smoothScrollTo((int) mFocusedButton.getX(),
- (int) mFocusedButton.getY());
- }
- }
-
Rect getPipMenuContainerBounds(Rect pipBounds) {
final Rect menuUiBounds = new Rect(pipBounds);
menuUiBounds.inset(-mPipMenuOuterSpace, -mPipMenuOuterSpace);
- menuUiBounds.bottom += mEduTextHeight;
+ menuUiBounds.bottom += mEduTextDrawer.getHeight();
return menuUiBounds;
}
@@ -420,78 +254,78 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mPipView.setLayoutParams(pipViewParams);
}
-
- }
-
- void setListener(@Nullable Listener listener) {
- mListener = listener;
- }
-
- void setExpandedModeEnabled(boolean enabled) {
- mExpandButton.setVisibility(enabled ? VISIBLE : GONE);
- }
-
- void setIsExpanded(boolean expanded) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setIsExpanded, expanded: %b", TAG, expanded);
+ // Keep focused button within the visible area while the PiP is changing size. Otherwise,
+ // the button would lose focus which would cause a need for scrolling and re-focusing after
+ // the animation finishes, which does not look good.
+ View focusedChild = mActionButtonsRecyclerView.getFocusedChild();
+ if (focusedChild != null) {
+ mActionButtonsRecyclerView.scrollToPosition(
+ mActionButtonsRecyclerView.getChildLayoutPosition(focusedChild));
}
- mExpandButton.setImageResource(
- expanded ? R.drawable.pip_ic_collapse : R.drawable.pip_ic_expand);
- mExpandButton.setTextAndDescription(
- expanded ? R.string.pip_collapse : R.string.pip_expand);
}
/**
* @param gravity for the arrow hints
*/
void showMoveMenu(int gravity) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
- }
- mButtonMenuIsVisible = false;
- mMoveMenuIsVisible = true;
- showButtonsMenu(false);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
showMovementHints(gravity);
+ setMenuButtonsVisible(false);
setFrameHighlighted(true);
+
+ animateAlphaTo(mA11yManager.isEnabled() ? 1f : 0f, mDimLayer);
+
+ mEduTextDrawer.closeIfNeeded();
}
- void showButtonsMenu() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showButtonsMenu()", TAG);
- }
- mButtonMenuIsVisible = true;
- mMoveMenuIsVisible = false;
- showButtonsMenu(true);
+ void showButtonsMenu(boolean exitingMoveMode) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showButtonsMenu(), exitingMoveMode %b", TAG, exitingMoveMode);
+ setMenuButtonsVisible(true);
hideMovementHints();
setFrameHighlighted(true);
+ animateAlphaTo(1f, mDimLayer);
+ mEduTextDrawer.closeIfNeeded();
- // Always focus on the first button when opening the menu, except directly after moving.
- if (mFocusedButton == null) {
- // Focus on first button (there is a Space at position 0)
- mFocusedButton = mActionButtonsContainer.getChildAt(1);
- // Reset scroll position.
- mScrollView.scrollTo(0, 0);
- mHorizontalScrollView.scrollTo(
- isLayoutRtl() ? mActionButtonsContainer.getWidth() : 0, 0);
+ if (exitingMoveMode) {
+ scrollAndRefocusButton(mTvPipActionsProvider.getFirstIndexOfAction(ACTION_MOVE),
+ /* alwaysScroll= */ false);
+ } else {
+ scrollAndRefocusButton(0, /* alwaysScroll= */ true);
+ }
+ }
+
+ private void scrollAndRefocusButton(int position, boolean alwaysScroll) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: scrollAndRefocusButton, target: %d", TAG, position);
+
+ if (alwaysScroll || !refocusButton(position)) {
+ mButtonLayoutManager.scrollToPositionWithOffset(position, 0);
+ mActionButtonsRecyclerView.post(() -> refocusButton(position));
}
- refocusPreviousButton();
}
/**
- * Hides all menu views, including the menu frame.
+ * @return true if focus was requested, false if focus request could not be carried out due to
+ * the view for the position not being available (scrolling beforehand will be necessary).
*/
+ private boolean refocusButton(int position) {
+ View itemToFocus = mButtonLayoutManager.findViewByPosition(position);
+ if (itemToFocus != null) {
+ itemToFocus.requestFocus();
+ itemToFocus.requestAccessibilityFocus();
+ }
+ return itemToFocus != null;
+ }
+
void hideAllUserControls() {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: hideAllUserControls()", TAG);
- mFocusedButton = null;
- mButtonMenuIsVisible = false;
- mMoveMenuIsVisible = false;
- showButtonsMenu(false);
+ setMenuButtonsVisible(false);
hideMovementHints();
setFrameHighlighted(false);
+ animateAlphaTo(0f, mDimLayer);
}
@Override
@@ -522,143 +356,30 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
});
}
- /**
- * Button order:
- * - Fullscreen
- * - Close
- * - Custom actions (app or media actions)
- * - System actions
- */
- void setAdditionalActions(List<RemoteAction> actions, RemoteAction closeAction,
- Handler mainHandler) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setAdditionalActions()", TAG);
- }
-
- // Replace system close action with custom close action if available
- if (closeAction != null) {
- setActionForButton(closeAction, mCloseButton, mainHandler);
- } else {
- mCloseButton.setTextAndDescription(R.string.pip_close);
- mCloseButton.setImageResource(R.drawable.pip_ic_close_white);
- }
- mCloseButton.setIsCustomCloseAction(closeAction != null);
- // Make sure the close action is always enabled
- mCloseButton.setEnabled(true);
-
- // Make sure we exactly as many additional buttons as we have actions to display.
- final int actionsNumber = actions.size();
- int buttonsNumber = mAdditionalButtons.size();
- if (actionsNumber > buttonsNumber) {
- // Add buttons until we have enough to display all the actions.
- while (actionsNumber > buttonsNumber) {
- TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
- button.setOnClickListener(this);
-
- mActionButtonsContainer.addView(button,
- FIRST_CUSTOM_ACTION_POSITION + buttonsNumber);
- mAdditionalButtons.add(button);
-
- buttonsNumber++;
- }
- } else if (actionsNumber < buttonsNumber) {
- // Hide buttons until we as many as the actions.
- while (actionsNumber < buttonsNumber) {
- final View button = mAdditionalButtons.get(buttonsNumber - 1);
- button.setVisibility(View.GONE);
- button.setTag(null);
-
- buttonsNumber--;
- }
- }
-
- // "Assign" actions to the buttons.
- for (int index = 0; index < actionsNumber; index++) {
- final RemoteAction action = actions.get(index);
- final TvPipMenuActionButton button = mAdditionalButtons.get(index);
-
- // Remove action if it matches the custom close action.
- if (PipUtils.remoteActionsMatch(action, closeAction)) {
- button.setVisibility(GONE);
- continue;
- }
- setActionForButton(action, button, mainHandler);
- }
-
- if (mCurrentPipBounds != null) {
- updateButtonGravity(mCurrentPipBounds);
- refocusPreviousButton();
- }
- }
-
- private void setActionForButton(RemoteAction action, TvPipMenuActionButton button,
- Handler mainHandler) {
- button.setVisibility(View.VISIBLE); // Ensure the button is visible.
- if (action.getContentDescription().length() > 0) {
- button.setTextAndDescription(action.getContentDescription());
- } else {
- button.setTextAndDescription(action.getTitle());
- }
- button.setEnabled(action.isEnabled());
- button.setTag(action);
- action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
- }
-
- @Nullable
- SurfaceControl getWindowSurfaceControl() {
- final ViewRootImpl root = getViewRootImpl();
- if (root == null) {
- return null;
- }
- final SurfaceControl out = root.getSurfaceControl();
- if (out != null && out.isValid()) {
- return out;
- }
- return null;
- }
-
@Override
- public void onClick(View v) {
- if (mListener == null) return;
-
- final int id = v.getId();
- if (id == R.id.tv_pip_menu_fullscreen_button) {
- mListener.onFullscreenButtonClick();
- } else if (id == R.id.tv_pip_menu_move_button) {
- mListener.onEnterMoveMode();
- } else if (id == R.id.tv_pip_menu_close_button) {
- mListener.onCloseButtonClick();
- } else if (id == R.id.tv_pip_menu_expand_button) {
- mListener.onToggleExpandedMode();
- } else {
- // This should be an "additional action"
- final RemoteAction action = (RemoteAction) v.getTag();
- if (action != null) {
- try {
- action.getActionIntent().send();
- } catch (PendingIntent.CanceledException e) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Failed to send action, %s", TAG, e);
- }
- } else {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: RemoteAction is null", TAG);
- }
+ public void onActionsChanged(int added, int updated, int startIndex) {
+ mRecyclerViewAdapter.notifyItemRangeChanged(startIndex, updated);
+ if (added > 0) {
+ mRecyclerViewAdapter.notifyItemRangeInserted(startIndex + updated, added);
+ } else if (added < 0) {
+ mRecyclerViewAdapter.notifyItemRangeRemoved(startIndex + updated, -added);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (mListener != null && event.getAction() == ACTION_UP) {
- if (!mMoveMenuIsVisible) {
- mFocusedButton = mActionButtonsContainer.getFocusedChild();
+ if (event.getAction() == ACTION_UP) {
+
+ if (event.getKeyCode() == KEYCODE_BACK) {
+ mListener.onBackPress();
+ return true;
+ }
+
+ if (mA11yManager.isEnabled()) {
+ return super.dispatchKeyEvent(event);
}
switch (event.getKeyCode()) {
- case KEYCODE_BACK:
- mListener.onBackPress();
- return true;
case KEYCODE_DPAD_UP:
case KEYCODE_DPAD_DOWN:
case KEYCODE_DPAD_LEFT:
@@ -679,15 +400,39 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
* Shows user hints for moving the PiP, e.g. arrows.
*/
public void showMovementHints(int gravity) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showMovementHints(), position: %s", TAG, Gravity.toString(gravity));
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showMovementHints(), position: %s", TAG, Gravity.toString(gravity));
+ mMoveMenuIsVisible = true;
animateAlphaTo(checkGravity(gravity, Gravity.BOTTOM) ? 1f : 0f, mArrowUp);
animateAlphaTo(checkGravity(gravity, Gravity.TOP) ? 1f : 0f, mArrowDown);
animateAlphaTo(checkGravity(gravity, Gravity.RIGHT) ? 1f : 0f, mArrowLeft);
animateAlphaTo(checkGravity(gravity, Gravity.LEFT) ? 1f : 0f, mArrowRight);
+
+ boolean a11yEnabled = mA11yManager.isEnabled();
+ setArrowA11yEnabled(mArrowUp, a11yEnabled, KEYCODE_DPAD_UP);
+ setArrowA11yEnabled(mArrowDown, a11yEnabled, KEYCODE_DPAD_DOWN);
+ setArrowA11yEnabled(mArrowLeft, a11yEnabled, KEYCODE_DPAD_LEFT);
+ setArrowA11yEnabled(mArrowRight, a11yEnabled, KEYCODE_DPAD_RIGHT);
+
+ animateAlphaTo(a11yEnabled ? 1f : 0f, mA11yDoneButton);
+ if (a11yEnabled) {
+ mA11yDoneButton.setVisibility(VISIBLE);
+ mA11yDoneButton.setOnClickListener(v -> {
+ mListener.onExitMoveMode();
+ });
+ mA11yDoneButton.requestFocus();
+ mA11yDoneButton.requestAccessibilityFocus();
+ }
+ }
+
+ private void setArrowA11yEnabled(View arrowView, boolean enabled, int keycode) {
+ arrowView.setClickable(enabled);
+ if (enabled) {
+ arrowView.setOnClickListener(v -> {
+ mListener.onPipMovement(keycode);
+ });
+ }
}
private boolean checkGravity(int gravity, int feature) {
@@ -698,40 +443,84 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
* Hides user hints for moving the PiP, e.g. arrows.
*/
public void hideMovementHints() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: hideMovementHints()", TAG);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: hideMovementHints()", TAG);
+
+ if (!mMoveMenuIsVisible) {
+ return;
}
+ mMoveMenuIsVisible = false;
+
animateAlphaTo(0, mArrowUp);
animateAlphaTo(0, mArrowRight);
animateAlphaTo(0, mArrowDown);
animateAlphaTo(0, mArrowLeft);
+ animateAlphaTo(0, mA11yDoneButton);
}
/**
* Show or hide the pip buttons menu.
*/
- public void showButtonsMenu(boolean show) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: showUserActions: %b", TAG, show);
- }
- if (show) {
- mActionButtonsContainer.setVisibility(VISIBLE);
- refocusPreviousButton();
- }
- animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
+ private void setMenuButtonsVisible(boolean visible) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showUserActions: %b", TAG, visible);
+ mButtonMenuIsVisible = visible;
+ animateAlphaTo(visible ? 1 : 0, mActionButtonsRecyclerView);
}
private void setFrameHighlighted(boolean highlighted) {
mMenuFrameView.setActivated(highlighted);
}
- interface Listener {
+ private class RecyclerViewAdapter extends
+ RecyclerView.Adapter<RecyclerViewAdapter.ButtonViewHolder> {
- void onBackPress();
+ private final List<TvPipAction> mActionList;
- void onEnterMoveMode();
+ RecyclerViewAdapter(List<TvPipAction> actionList) {
+ this.mActionList = actionList;
+ }
+
+ @NonNull
+ @Override
+ public ButtonViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ButtonViewHolder(new TvWindowMenuActionButton(mContext));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ButtonViewHolder holder, int position) {
+ TvPipAction action = mActionList.get(position);
+ action.populateButton(holder.mButton, mMainHandler);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mActionList.size();
+ }
+
+ private class ButtonViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
+ TvWindowMenuActionButton mButton;
+
+ ButtonViewHolder(@NonNull View itemView) {
+ super(itemView);
+ mButton = (TvWindowMenuActionButton) itemView;
+ mButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ TvPipAction action = mActionList.get(
+ mActionButtonsRecyclerView.getChildLayoutPosition(v));
+ if (action != null) {
+ action.executeAction();
+ }
+ }
+ }
+ }
+
+ interface Listener extends TvPipMenuEduTextDrawer.Listener {
+
+ void onBackPress();
/**
* Called when a button for exiting move mode was pressed.
@@ -745,11 +534,5 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
* @return whether pip movement was handled.
*/
boolean onPipMovement(int keycode);
-
- void onCloseButtonClick();
-
- void onFullscreenButtonClick();
-
- void onToggleExpandedMode();
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 61a609d9755e..f22ee595e6c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -16,18 +16,13 @@
package com.android.wm.shell.pip.tv;
-import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
-import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
-
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -35,7 +30,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Bundle;
-import android.os.Handler;
import android.text.TextUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -47,7 +41,6 @@ import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -55,39 +48,18 @@ import java.util.List;
* <p>Once it's created, it will manage the PiP notification UI by itself except for handling
* configuration changes and user initiated expanded PiP toggling.
*/
-public class TvPipNotificationController {
- private static final String TAG = "TvPipNotification";
+public class TvPipNotificationController implements TvPipActionsProvider.Listener {
+ private static final String TAG = TvPipNotificationController.class.getSimpleName();
// Referenced in com.android.systemui.util.NotificationChannels.
public static final String NOTIFICATION_CHANNEL = "TVPIP";
private static final String NOTIFICATION_TAG = "TvPip";
- private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
-
- private static final String ACTION_SHOW_PIP_MENU =
- "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
- private static final String ACTION_CLOSE_PIP =
- "com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
- private static final String ACTION_MOVE_PIP =
- "com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
- private static final String ACTION_TOGGLE_EXPANDED_PIP =
- "com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
- private static final String ACTION_FULLSCREEN =
- "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
private final Context mContext;
private final PackageManager mPackageManager;
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder;
- private final ActionBroadcastReceiver mActionBroadcastReceiver;
- private final Handler mMainHandler;
- private Delegate mDelegate;
- private final TvPipBoundsState mTvPipBoundsState;
-
- private String mDefaultTitle;
-
- private final List<RemoteAction> mCustomActions = new ArrayList<>();
- private final List<RemoteAction> mMediaActions = new ArrayList<>();
- private RemoteAction mCustomCloseAction;
+ private TvPipActionsProvider mTvPipActionsProvider;
private MediaSession.Token mMediaSessionToken;
@@ -95,55 +67,41 @@ public class TvPipNotificationController {
private String mPackageName;
private boolean mIsNotificationShown;
+ private String mDefaultTitle;
private String mPipTitle;
private String mPipSubtitle;
+ // Saving the actions, so they don't have to be regenerated when e.g. the PiP title changes.
+ @NonNull
+ private Notification.Action[] mPipActions;
+
private Bitmap mActivityIcon;
public TvPipNotificationController(Context context, PipMediaController pipMediaController,
- PipParamsChangedForwarder pipParamsChangedForwarder, TvPipBoundsState tvPipBoundsState,
- Handler mainHandler) {
+ PipParamsChangedForwarder pipParamsChangedForwarder) {
mContext = context;
mPackageManager = context.getPackageManager();
mNotificationManager = context.getSystemService(NotificationManager.class);
- mMainHandler = mainHandler;
- mTvPipBoundsState = tvPipBoundsState;
+
+ mPipActions = new Notification.Action[0];
mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
.setLocalOnly(true)
.setOngoing(true)
.setCategory(Notification.CATEGORY_SYSTEM)
.setShowWhen(true)
+ .setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.pip_icon)
.setAllowSystemGeneratedContextualActions(false)
- .setContentIntent(createPendingIntent(context, ACTION_FULLSCREEN))
- .setDeleteIntent(getCloseAction().actionIntent)
- .extend(new Notification.TvExtender()
- .setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU))
- .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP)));
-
- mActionBroadcastReceiver = new ActionBroadcastReceiver();
+ .setContentIntent(
+ createPendingIntent(context, TvPipController.ACTION_TO_FULLSCREEN));
+ // TvExtender and DeleteIntent set later since they might change.
- pipMediaController.addActionListener(this::onMediaActionsChanged);
pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
pipParamsChangedForwarder.addListener(
new PipParamsChangedForwarder.PipParamsChangedCallback() {
@Override
- public void onExpandedAspectRatioChanged(float ratio) {
- updateExpansionState();
- }
-
- @Override
- public void onActionsChanged(List<RemoteAction> actions,
- RemoteAction closeAction) {
- mCustomActions.clear();
- mCustomActions.addAll(actions);
- mCustomCloseAction = closeAction;
- updateNotificationContent();
- }
-
- @Override
public void onTitleChanged(String title) {
mPipTitle = title;
updateNotificationContent();
@@ -156,34 +114,33 @@ public class TvPipNotificationController {
}
});
- onConfigurationChanged(context);
+ onConfigurationChanged();
}
- void setDelegate(Delegate delegate) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
- TAG, delegate);
-
- if (mDelegate != null) {
- throw new IllegalStateException(
- "The delegate has already been set and should not change.");
- }
- if (delegate == null) {
- throw new IllegalArgumentException("The delegate must not be null.");
- }
+ /**
+ * Call before showing any notification.
+ */
+ void setTvPipActionsProvider(@NonNull TvPipActionsProvider tvPipActionsProvider) {
+ mTvPipActionsProvider = tvPipActionsProvider;
+ mTvPipActionsProvider.addListener(this);
+ }
- mDelegate = delegate;
+ void onConfigurationChanged() {
+ mDefaultTitle = mContext.getResources().getString(R.string.pip_notification_unknown_title);
+ updateNotificationContent();
}
void show(String packageName) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
- if (mDelegate == null) {
- throw new IllegalStateException("Delegate is not set.");
+ if (mTvPipActionsProvider == null) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Missing TvPipActionsProvider", TAG);
+ return;
}
mIsNotificationShown = true;
mPackageName = packageName;
mActivityIcon = getActivityIcon();
- mActionBroadcastReceiver.register();
updateNotificationContent();
}
@@ -193,151 +150,42 @@ public class TvPipNotificationController {
mIsNotificationShown = false;
mPackageName = null;
- mActionBroadcastReceiver.unregister();
-
mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
}
- private Notification.Action getToggleAction(boolean expanded) {
- if (expanded) {
- return createSystemAction(R.drawable.pip_ic_collapse,
- R.string.pip_collapse, ACTION_TOGGLE_EXPANDED_PIP);
- } else {
- return createSystemAction(R.drawable.pip_ic_expand, R.string.pip_expand,
- ACTION_TOGGLE_EXPANDED_PIP);
- }
- }
-
- private Notification.Action createSystemAction(int iconRes, int titleRes, String action) {
- Notification.Action.Builder builder = new Notification.Action.Builder(
- Icon.createWithResource(mContext, iconRes),
- mContext.getString(titleRes),
- createPendingIntent(mContext, action));
- builder.setContextual(true);
- return builder.build();
- }
-
- private void onMediaActionsChanged(List<RemoteAction> actions) {
- mMediaActions.clear();
- mMediaActions.addAll(actions);
- if (mCustomActions.isEmpty()) {
- updateNotificationContent();
- }
- }
-
private void onMediaSessionTokenChanged(MediaSession.Token token) {
mMediaSessionToken = token;
updateNotificationContent();
}
- private Notification.Action remoteToNotificationAction(RemoteAction action) {
- return remoteToNotificationAction(action, SEMANTIC_ACTION_NONE);
- }
-
- private Notification.Action remoteToNotificationAction(RemoteAction action,
- int semanticAction) {
- Notification.Action.Builder builder = new Notification.Action.Builder(action.getIcon(),
- action.getTitle(),
- action.getActionIntent());
- if (action.getContentDescription() != null) {
- Bundle extras = new Bundle();
- extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
- action.getContentDescription());
- builder.addExtras(extras);
- }
- builder.setSemanticAction(semanticAction);
- builder.setContextual(true);
- return builder.build();
- }
-
- private Notification.Action[] getNotificationActions() {
- final List<Notification.Action> actions = new ArrayList<>();
-
- // 1. Fullscreen
- actions.add(getFullscreenAction());
- // 2. Close
- actions.add(getCloseAction());
- // 3. App actions
- final List<RemoteAction> appActions =
- mCustomActions.isEmpty() ? mMediaActions : mCustomActions;
- for (RemoteAction appAction : appActions) {
- if (PipUtils.remoteActionsMatch(mCustomCloseAction, appAction)
- || !appAction.isEnabled()) {
- continue;
- }
- actions.add(remoteToNotificationAction(appAction));
- }
- // 4. Move
- actions.add(getMoveAction());
- // 5. Toggle expansion (if expanded PiP enabled)
- if (mTvPipBoundsState.getDesiredTvExpandedAspectRatio() > 0
- && mTvPipBoundsState.isTvExpandedPipSupported()) {
- actions.add(getToggleAction(mTvPipBoundsState.isTvPipExpanded()));
- }
- return actions.toArray(new Notification.Action[0]);
- }
-
- private Notification.Action getCloseAction() {
- if (mCustomCloseAction == null) {
- return createSystemAction(R.drawable.pip_ic_close_white, R.string.pip_close,
- ACTION_CLOSE_PIP);
- } else {
- return remoteToNotificationAction(mCustomCloseAction, SEMANTIC_ACTION_DELETE);
- }
- }
-
- private Notification.Action getFullscreenAction() {
- return createSystemAction(R.drawable.pip_ic_fullscreen_white,
- R.string.pip_fullscreen, ACTION_FULLSCREEN);
- }
-
- private Notification.Action getMoveAction() {
- return createSystemAction(R.drawable.pip_ic_move_white, R.string.pip_move,
- ACTION_MOVE_PIP);
- }
-
- /**
- * Called by {@link TvPipController} when the configuration is changed.
- */
- void onConfigurationChanged(Context context) {
- mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title);
- updateNotificationContent();
- }
-
- void updateExpansionState() {
- updateNotificationContent();
- }
-
private void updateNotificationContent() {
if (mPackageManager == null || !mIsNotificationShown) {
return;
}
- Notification.Action[] actions = getNotificationActions();
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: update(), title: %s, subtitle: %s, mediaSessionToken: %s, #actions: %s", TAG,
- getNotificationTitle(), mPipSubtitle, mMediaSessionToken, actions.length);
- for (Notification.Action action : actions) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: action: %s", TAG,
- action.toString());
- }
-
+ getNotificationTitle(), mPipSubtitle, mMediaSessionToken, mPipActions.length);
mNotificationBuilder
.setWhen(System.currentTimeMillis())
.setContentTitle(getNotificationTitle())
.setContentText(mPipSubtitle)
.setSubText(getApplicationLabel(mPackageName))
- .setActions(actions);
+ .setActions(mPipActions);
setPipIcon();
Bundle extras = new Bundle();
extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mMediaSessionToken);
mNotificationBuilder.setExtras(extras);
+ PendingIntent closeIntent = mTvPipActionsProvider.getCloseAction().getPendingIntent();
+ mNotificationBuilder.setDeleteIntent(closeIntent);
// TvExtender not recognized if not set last.
mNotificationBuilder.extend(new Notification.TvExtender()
- .setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
- .setDeleteIntent(createPendingIntent(mContext, ACTION_CLOSE_PIP)));
+ .setContentIntent(
+ createPendingIntent(mContext, TvPipController.ACTION_SHOW_PIP_MENU))
+ .setDeleteIntent(closeIntent));
+
mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
mNotificationBuilder.build());
}
@@ -389,68 +237,20 @@ public class TvPipNotificationController {
return ImageUtils.buildScaledBitmap(drawable, width, height, /* allowUpscaling */ true);
}
- private static PendingIntent createPendingIntent(Context context, String action) {
+ static PendingIntent createPendingIntent(Context context, String action) {
return PendingIntent.getBroadcast(context, 0,
new Intent(action).setPackage(context.getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
- private class ActionBroadcastReceiver extends BroadcastReceiver {
- final IntentFilter mIntentFilter;
- {
- mIntentFilter = new IntentFilter();
- mIntentFilter.addAction(ACTION_CLOSE_PIP);
- mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
- mIntentFilter.addAction(ACTION_MOVE_PIP);
- mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
- mIntentFilter.addAction(ACTION_FULLSCREEN);
- }
- boolean mRegistered = false;
-
- void register() {
- if (mRegistered) return;
-
- mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
- mMainHandler);
- mRegistered = true;
- }
-
- void unregister() {
- if (!mRegistered) return;
-
- mContext.unregisterReceiver(this);
- mRegistered = false;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: on(Broadcast)Receive(), action=%s", TAG, action);
-
- if (ACTION_SHOW_PIP_MENU.equals(action)) {
- mDelegate.showPictureInPictureMenu();
- } else if (ACTION_CLOSE_PIP.equals(action)) {
- mDelegate.closePip();
- } else if (ACTION_MOVE_PIP.equals(action)) {
- mDelegate.enterPipMovementMenu();
- } else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
- mDelegate.togglePipExpansion();
- } else if (ACTION_FULLSCREEN.equals(action)) {
- mDelegate.movePipToFullscreen();
- }
+ @Override
+ public void onActionsChanged(int added, int updated, int startIndex) {
+ List<TvPipAction> actions = mTvPipActionsProvider.getActionsList();
+ mPipActions = new Notification.Action[actions.size()];
+ for (int i = 0; i < mPipActions.length; i++) {
+ mPipActions[i] = actions.get(i).toNotificationAction(mContext);
}
+ updateNotificationContent();
}
- interface Delegate {
- void showPictureInPictureMenu();
-
- void closePip();
-
- void enterPipMovementMenu();
-
- void togglePipExpansion();
-
- void movePipToFullscreen();
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java
new file mode 100644
index 000000000000..93b6a908e3f4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipSystemAction.java
@@ -0,0 +1,83 @@
+/*
+ * 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.pip.tv;
+
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+
+import com.android.wm.shell.common.TvWindowMenuActionButton;
+
+/**
+ * A TvPipAction for actions that the system provides, i.e. fullscreen, default close, move,
+ * expand/collapse.
+ */
+public class TvPipSystemAction extends TvPipAction {
+
+ @StringRes
+ private int mTitleResource;
+ @DrawableRes
+ private int mIconResource;
+
+ private final PendingIntent mBroadcastIntent;
+
+ TvPipSystemAction(@ActionType int actionType, @StringRes int title, @DrawableRes int icon,
+ String broadcastAction, @NonNull Context context,
+ SystemActionsHandler systemActionsHandler) {
+ super(actionType, systemActionsHandler);
+ update(title, icon);
+ mBroadcastIntent = TvPipNotificationController.createPendingIntent(context,
+ broadcastAction);
+ }
+
+ void update(@StringRes int title, @DrawableRes int icon) {
+ mTitleResource = title;
+ mIconResource = icon;
+ }
+
+ void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler) {
+ button.setTextAndDescription(mTitleResource);
+ button.setImageResource(mIconResource);
+ button.setEnabled(true);
+ }
+
+ PendingIntent getPendingIntent() {
+ return mBroadcastIntent;
+ }
+
+ @Override
+ Notification.Action toNotificationAction(Context context) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(
+ Icon.createWithResource(context, mIconResource),
+ context.getString(mTitleResource),
+ mBroadcastIntent);
+
+ builder.setSemanticAction(isCloseAction()
+ ? SEMANTIC_ACTION_DELETE : SEMANTIC_ACTION_NONE);
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
new file mode 100644
index 000000000000..28be0efc38f6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
@@ -0,0 +1,3 @@
+# WM shell sub-module TV splitscreen owner
+galinap@google.com
+bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
new file mode 100644
index 000000000000..1d8a8d506c5c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
@@ -0,0 +1,218 @@
+/*
+ * 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.splitscreen.tv;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+/**
+ * Handles the interaction logic with the {@link TvSplitMenuView}.
+ * A bridge between {@link TvStageCoordinator} and {@link TvSplitMenuView}.
+ */
+public class TvSplitMenuController implements TvSplitMenuView.Listener {
+
+ private static final String TAG = TvSplitMenuController.class.getSimpleName();
+ private static final String ACTION_SHOW_MENU = "com.android.wm.shell.splitscreen.SHOW_MENU";
+ private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
+
+ private final Context mContext;
+ private final StageController mStageController;
+ private final SystemWindows mSystemWindows;
+ private final Handler mMainHandler;
+
+ private final TvSplitMenuView mSplitMenuView;
+
+ private final ActionBroadcastReceiver mActionBroadcastReceiver;
+
+ private final int mTvButtonFadeAnimationDuration;
+
+ public TvSplitMenuController(Context context, StageController stageController,
+ SystemWindows systemWindows, Handler mainHandler) {
+ mContext = context;
+ mMainHandler = mainHandler;
+ mStageController = stageController;
+ mSystemWindows = systemWindows;
+
+ mTvButtonFadeAnimationDuration = context.getResources()
+ .getInteger(R.integer.tv_window_menu_fade_animation_duration);
+
+ mSplitMenuView = (TvSplitMenuView) LayoutInflater.from(context)
+ .inflate(R.layout.tv_split_menu_view, null);
+ mSplitMenuView.setListener(this);
+
+ mActionBroadcastReceiver = new ActionBroadcastReceiver();
+ }
+
+ /**
+ * Adds the menu view for the splitscreen to SystemWindows.
+ */
+ void addSplitMenuViewToSystemWindows() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mContext.getResources().getDisplayMetrics().widthPixels,
+ mContext.getResources().getDisplayMetrics().heightPixels,
+ TYPE_DOCK_DIVIDER,
+ FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ mSplitMenuView.setAlpha(0);
+ mSystemWindows.addView(mSplitMenuView, lp, DEFAULT_DISPLAY, SHELL_ROOT_LAYER_DIVIDER);
+ }
+
+ /**
+ * Removes the menu view for the splitscreen from SystemWindows.
+ */
+ void removeSplitMenuViewFromSystemWindows() {
+ mSystemWindows.removeView(mSplitMenuView);
+ }
+
+ /**
+ * Registers BroadcastReceiver when split screen mode is entered.
+ */
+ void registerBroadcastReceiver() {
+ mActionBroadcastReceiver.register();
+ }
+
+ /**
+ * Unregisters BroadcastReceiver when split screen mode is entered.
+ */
+ void unregisterBroadcastReceiver() {
+ mActionBroadcastReceiver.unregister();
+ }
+
+ @Override
+ public void onBackPress() {
+ setMenuVisibility(false);
+ }
+
+ @Override
+ public void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus) {
+ setMenuVisibility(false);
+ mStageController.grantFocusToStage(stageToFocus);
+ }
+
+ @Override
+ public void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose) {
+ setMenuVisibility(false);
+ mStageController.exitStage(stageToClose);
+ }
+
+ @Override
+ public void onSwapPress() {
+ mStageController.swapStages();
+ }
+
+ private void setMenuVisibility(boolean visible) {
+ applyMenuVisibility(visible);
+ setMenuFocus(visible);
+ }
+
+ private void applyMenuVisibility(boolean visible) {
+ float alphaTarget = visible ? 1F : 0F;
+
+ if (mSplitMenuView.getAlpha() == alphaTarget) {
+ return;
+ }
+
+ mSplitMenuView.animate()
+ .alpha(alphaTarget)
+ .setDuration(mTvButtonFadeAnimationDuration)
+ .withStartAction(() -> {
+ if (alphaTarget != 0) {
+ mSplitMenuView.setVisibility(VISIBLE);
+ }
+ })
+ .withEndAction(() -> {
+ if (alphaTarget == 0) {
+ mSplitMenuView.setVisibility(INVISIBLE);
+ }
+ });
+
+ }
+
+ private void setMenuFocus(boolean focused) {
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null,
+ mSystemWindows.getFocusGrantToken(mSplitMenuView), focused);
+ } catch (RemoteException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "%s: Unable to update focus, %s", TAG, e);
+ }
+ }
+
+ interface StageController {
+ void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus);
+ void exitStage(@SplitScreenConstants.SplitPosition int stageToClose);
+ void swapStages();
+ }
+
+ private class ActionBroadcastReceiver extends BroadcastReceiver {
+
+ final IntentFilter mIntentFilter;
+ {
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_SHOW_MENU);
+ }
+ boolean mRegistered = false;
+
+ void register() {
+ if (mRegistered) return;
+
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
+ mMainHandler);
+ mRegistered = true;
+ }
+
+ void unregister() {
+ if (!mRegistered) return;
+
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (ACTION_SHOW_MENU.equals(action)) {
+ setMenuVisibility(true);
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
new file mode 100644
index 000000000000..88e9757a9b31
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
@@ -0,0 +1,117 @@
+/*
+ * 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.splitscreen.tv;
+
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_BACK;
+
+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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+
+/**
+ * A View for the Menu Window.
+ */
+public class TvSplitMenuView extends LinearLayout implements View.OnClickListener {
+
+ private Listener mListener;
+
+ public TvSplitMenuView(Context context) {
+ super(context);
+ }
+
+ public TvSplitMenuView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public TvSplitMenuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ initButtons();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+
+ final int id = v.getId();
+ if (id == R.id.tv_split_main_menu_focus_button) {
+ mListener.onFocusStage(SPLIT_POSITION_TOP_OR_LEFT);
+ } else if (id == R.id.tv_split_main_menu_close_button) {
+ mListener.onCloseStage(SPLIT_POSITION_TOP_OR_LEFT);
+ } else if (id == R.id.tv_split_side_menu_focus_button) {
+ mListener.onFocusStage(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ } else if (id == R.id.tv_split_side_menu_close_button) {
+ mListener.onCloseStage(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ } else if (id == R.id.tv_split_menu_swap_stages) {
+ mListener.onSwapPress();
+ }
+ }
+
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == ACTION_DOWN) {
+ if (event.getKeyCode() == KEYCODE_BACK) {
+ if (mListener != null) {
+ mListener.onBackPress();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ private void initButtons() {
+ findViewById(R.id.tv_split_main_menu_focus_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_main_menu_close_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_side_menu_focus_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_side_menu_close_button).setOnClickListener(this);
+ findViewById(R.id.tv_split_menu_swap_stages).setOnClickListener(this);
+ }
+
+ void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ interface Listener {
+ /** "Back" button from the remote control */
+ void onBackPress();
+
+ /** Menu Action Buttons */
+
+ void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus);
+
+ void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose);
+
+ void onSwapPress();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
new file mode 100644
index 000000000000..46d2a5a11671
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -0,0 +1,117 @@
+/*
+ * 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.splitscreen.tv;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.launcher3.icons.IconProvider;
+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.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+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.transition.Transitions;
+
+import java.util.Optional;
+
+/**
+ * Class inherits from {@link SplitScreenController} and provides {@link TvStageCoordinator}
+ * for Split Screen on TV.
+ */
+public class TvSplitScreenController extends SplitScreenController {
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final SyncTransactionQueue mSyncQueue;
+ private final Context mContext;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final Transitions mTransitions;
+ private final TransactionPool mTransactionPool;
+ private final IconProvider mIconProvider;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
+
+ private final Handler mMainHandler;
+ private final SystemWindows mSystemWindows;
+
+ public TvSplitScreenController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ DragAndDropController dragAndDropController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
+ ShellExecutor mainExecutor,
+ Handler mainHandler,
+ SystemWindows systemWindows) {
+ super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
+ syncQueue, rootTDAOrganizer, displayController, displayImeController,
+ displayInsetsController, dragAndDropController, transitions, transactionPool,
+ iconProvider, recentTasks, mainExecutor);
+
+ mTaskOrganizer = shellTaskOrganizer;
+ mSyncQueue = syncQueue;
+ mContext = context;
+ mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
+ mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
+ mTransitions = transitions;
+ mTransactionPool = transactionPool;
+ mIconProvider = iconProvider;
+ mRecentTasksOptional = recentTasks;
+
+ mMainHandler = mainHandler;
+ mSystemWindows = systemWindows;
+ }
+
+ /**
+ * Provides Tv-specific StageCoordinator.
+ * @return {@link TvStageCoordinator}
+ */
+ @Override
+ protected StageCoordinator createStageCoordinator() {
+ return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
+ mTaskOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mTransitions, mTransactionPool,
+ mIconProvider, mMainExecutor, mMainHandler,
+ mRecentTasksOptional, mSystemWindows);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
new file mode 100644
index 000000000000..4d563fbb7f04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -0,0 +1,94 @@
+/*
+ * 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.splitscreen.tv;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitScreenConstants;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.Optional;
+
+/**
+ * Expands {@link StageCoordinator} functionality with Tv-specific methods.
+ */
+public class TvStageCoordinator extends StageCoordinator
+ implements TvSplitMenuController.StageController {
+
+ private final TvSplitMenuController mTvSplitMenuController;
+
+ public TvStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+ ShellTaskOrganizer taskOrganizer, DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController, Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider, ShellExecutor mainExecutor,
+ Handler mainHandler,
+ Optional<RecentTasksController> recentTasks,
+ SystemWindows systemWindows) {
+ super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
+ displayInsetsController, transitions, transactionPool, iconProvider,
+ mainExecutor, recentTasks);
+
+ mTvSplitMenuController = new TvSplitMenuController(context, this,
+ systemWindows, mainHandler);
+
+ }
+
+ @Override
+ protected void onSplitScreenEnter() {
+ mTvSplitMenuController.addSplitMenuViewToSystemWindows();
+ mTvSplitMenuController.registerBroadcastReceiver();
+ }
+
+ @Override
+ protected void onSplitScreenExit() {
+ mTvSplitMenuController.unregisterBroadcastReceiver();
+ mTvSplitMenuController.removeSplitMenuViewFromSystemWindows();
+ }
+
+ @Override
+ public void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus) {
+ super.grantFocusToStage(stageToFocus);
+ }
+
+ @Override
+ public void exitStage(@SplitScreenConstants.SplitPosition int stageToClose) {
+ super.exitStage(stageToClose);
+ }
+
+ /**
+ * Swaps the stages inside the SplitLayout.
+ */
+ @Override
+ public void swapStages() {
+ onDoubleTappedDivider();
+ }
+
+}
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 839d56a43222..ebb957b2201b 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
@@ -16,8 +16,10 @@
package com.android.wm.shell.startingsurface;
+import static android.content.Context.CONTEXT_RESTRICTED;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -29,6 +31,7 @@ import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -48,9 +51,11 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -58,7 +63,9 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.ContextThemeWrapper;
+import android.view.Display;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
@@ -134,6 +141,144 @@ public class SplashscreenContentDrawer {
}
/**
+ * Help method to create a layout parameters for a window.
+ */
+ static Context createContext(Context initContext, StartingWindowInfo windowInfo,
+ int theme, @StartingWindowInfo.StartingWindowType int suggestType,
+ DisplayManager displayManager) {
+ final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
+ final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
+ ? windowInfo.targetActivityInfo
+ : taskInfo.topActivityInfo;
+ if (activityInfo == null || activityInfo.packageName == null) {
+ return null;
+ }
+
+ final int displayId = taskInfo.displayId;
+ final int taskId = taskInfo.taskId;
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d",
+ activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType);
+ final Display display = displayManager.getDisplay(displayId);
+ if (display == null) {
+ // Can't show splash screen on requested display, so skip showing at all.
+ return null;
+ }
+ Context context = displayId == DEFAULT_DISPLAY
+ ? initContext : initContext.createDisplayContext(display);
+ if (context == null) {
+ return null;
+ }
+ if (theme != context.getThemeResId()) {
+ try {
+ context = context.createPackageContextAsUser(activityInfo.packageName,
+ CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
+ context.setTheme(theme);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Failed creating package context with package name "
+ + activityInfo.packageName + " for user " + taskInfo.userId, e);
+ return null;
+ }
+ }
+
+ final Configuration taskConfig = taskInfo.getConfiguration();
+ if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "addSplashScreen: creating context based on task Configuration %s",
+ taskConfig);
+ final Context overrideContext = context.createConfigurationContext(taskConfig);
+ overrideContext.setTheme(theme);
+ final TypedArray typedArray = overrideContext.obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+ try {
+ if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+ // We want to use the windowBackground for the override context if it is
+ // available, otherwise we use the default one to make sure a themed starting
+ // window is displayed for the app.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "addSplashScreen: apply overrideConfig %s",
+ taskConfig);
+ context = overrideContext;
+ }
+ } catch (Resources.NotFoundException e) {
+ Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
+ + taskId, e);
+ return null;
+ }
+ typedArray.recycle();
+ }
+ return context;
+ }
+
+ /**
+ * Creates the window layout parameters for splashscreen window.
+ */
+ static WindowManager.LayoutParams createLayoutParameters(Context context,
+ StartingWindowInfo windowInfo,
+ @StartingWindowInfo.StartingWindowType int suggestType,
+ CharSequence title, int pixelFormat, IBinder appToken) {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+ params.setFitInsetsSides(0);
+ params.setFitInsetsTypes(0);
+ params.format = pixelFormat;
+ int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+ final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
+ if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ }
+ if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+ if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ }
+ } else {
+ windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ }
+ params.layoutInDisplayCutoutMode = a.getInt(
+ R.styleable.Window_windowLayoutInDisplayCutoutMode,
+ params.layoutInDisplayCutoutMode);
+ params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
+ a.recycle();
+
+ final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
+ final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
+ ? windowInfo.targetActivityInfo
+ : taskInfo.topActivityInfo;
+ final int displayId = taskInfo.displayId;
+ // Assumes it's safe to show starting windows of launched apps while
+ // the keyguard is being hidden. This is okay because starting windows never show
+ // secret information.
+ // TODO(b/113840485): Occluded may not only happen on default display
+ if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+ }
+
+ // Force the window flags: this is a fake window, so it is not really
+ // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
+ // flag because we do know that the next window will take input
+ // focus, so we want to get the IME window up on top of us right away.
+ // Touches will only pass through to the host activity window and will be blocked from
+ // passing to any other windows.
+ windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ params.flags = windowFlags;
+ params.token = appToken;
+ params.packageName = activityInfo.packageName;
+ params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+
+ if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
+ params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+ }
+
+ params.setTitle("Splash Screen " + title);
+ return params;
+ }
+ /**
* Create a SplashScreenView object.
*
* In order to speed up the splash screen view to show on first frame, preparing the
@@ -248,6 +393,26 @@ public class SplashscreenContentDrawer {
return null;
}
+ /**
+ * Creates a SplashScreenView without read animatable icon and branding image.
+ */
+ SplashScreenView makeSimpleSplashScreenContentView(Context context,
+ StartingWindowInfo info, int themeBGColor) {
+ updateDensity();
+ mTmpAttrs.reset();
+ final ActivityInfo ai = info.targetActivityInfo != null
+ ? info.targetActivityInfo
+ : info.taskInfo.topActivityInfo;
+
+ final SplashViewBuilder builder = new SplashViewBuilder(context, ai);
+ final SplashScreenView view = builder
+ .setWindowBGColor(themeBGColor)
+ .chooseStyle(STARTING_WINDOW_TYPE_SPLASH_SCREEN)
+ .build();
+ view.setNotCopyable();
+ return view;
+ }
+
private SplashScreenView makeSplashScreenContentView(Context context, StartingWindowInfo info,
@StartingWindowType int suggestType, Consumer<Runnable> uiThreadInitConsumer) {
updateDensity();
@@ -263,7 +428,8 @@ public class SplashscreenContentDrawer {
final int themeBGColor = legacyDrawable != null
? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable))
: getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs));
- return new StartingWindowViewBuilder(context, ai)
+
+ return new SplashViewBuilder(context, ai)
.setWindowBGColor(themeBGColor)
.overlayDrawable(legacyDrawable)
.chooseStyle(suggestType)
@@ -322,6 +488,14 @@ public class SplashscreenContentDrawer {
private Drawable mSplashScreenIcon = null;
private Drawable mBrandingImage = null;
private int mIconBgColor = Color.TRANSPARENT;
+
+ void reset() {
+ mWindowBgResId = 0;
+ mWindowBgColor = Color.TRANSPARENT;
+ mSplashScreenIcon = null;
+ mBrandingImage = null;
+ mIconBgColor = Color.TRANSPARENT;
+ }
}
/**
@@ -351,7 +525,7 @@ public class SplashscreenContentDrawer {
return appReadyDuration;
}
- private class StartingWindowViewBuilder {
+ private class SplashViewBuilder {
private final Context mContext;
private final ActivityInfo mActivityInfo;
@@ -364,27 +538,28 @@ public class SplashscreenContentDrawer {
/** @see #setAllowHandleSolidColor(boolean) **/
private boolean mAllowHandleSolidColor;
- StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
+ SplashViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
mContext = context;
mActivityInfo = aInfo;
}
- StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) {
+ SplashViewBuilder setWindowBGColor(@ColorInt int background) {
mThemeColor = background;
return this;
}
- StartingWindowViewBuilder overlayDrawable(Drawable overlay) {
+ SplashViewBuilder overlayDrawable(Drawable overlay) {
mOverlayDrawable = overlay;
return this;
}
- StartingWindowViewBuilder chooseStyle(int suggestType) {
+ SplashViewBuilder chooseStyle(int suggestType) {
mSuggestType = suggestType;
return this;
}
- StartingWindowViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
+ // Set up the UI thread for the View.
+ SplashViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
mUiThreadInitTask = uiThreadInitTask;
return this;
}
@@ -395,7 +570,7 @@ public class SplashscreenContentDrawer {
* android.window.SplashScreen.OnExitAnimationListener#onSplashScreenExit(SplashScreenView)}
* callback, effectively copying the {@link SplashScreenView} into the client process.
*/
- StartingWindowViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) {
+ SplashViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) {
mAllowHandleSolidColor = allowHandleSolidColor;
return this;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 7f6bfd23f72b..e419462012e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -62,7 +62,7 @@ public class SplashscreenIconDrawableFactory {
*/
static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
@NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
- boolean loadInDetail, Handler splashscreenWorkerHandler) {
+ boolean loadInDetail, Handler preDrawHandler) {
Drawable foreground;
Drawable background = null;
boolean drawBackground =
@@ -74,13 +74,13 @@ public class SplashscreenIconDrawableFactory {
// If the icon is Adaptive, we already use the icon background.
drawBackground = false;
foreground = new ImmobileIconDrawable(foregroundDrawable,
- srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
+ srcIconSize, iconSize, loadInDetail, preDrawHandler);
} else {
// Adaptive icon don't handle transparency so we draw the background of the adaptive
// icon with the same color as the window background color instead of using two layers
foreground = new ImmobileIconDrawable(
new AdaptiveForegroundDrawable(foregroundDrawable),
- srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
+ srcIconSize, iconSize, loadInDetail, preDrawHandler);
}
if (drawBackground) {
@@ -91,9 +91,9 @@ public class SplashscreenIconDrawableFactory {
}
static Drawable[] makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
- int iconSize, boolean loadInDetail, Handler splashscreenWorkerHandler) {
+ int iconSize, boolean loadInDetail, Handler preDrawHandler) {
return new Drawable[]{new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
- loadInDetail, splashscreenWorkerHandler)};
+ loadInDetail, preDrawHandler)};
}
/**
@@ -107,14 +107,14 @@ public class SplashscreenIconDrawableFactory {
private Bitmap mIconBitmap;
ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize, boolean loadInDetail,
- Handler splashscreenWorkerHandler) {
+ Handler preDrawHandler) {
// This icon has lower density, don't scale it.
if (loadInDetail) {
- splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, iconSize));
+ preDrawHandler.post(() -> preDrawIcon(drawable, iconSize));
} else {
final float scale = (float) iconSize / srcIconSize;
mMatrix.setScale(scale, scale);
- splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize));
+ preDrawHandler.post(() -> preDrawIcon(drawable, srcIconSize));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 22e804547d5c..4f07bfeacce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.startingsurface;
-import static android.content.Context.CONTEXT_RESTRICTED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -32,8 +31,6 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
@@ -198,118 +195,21 @@ public class StartingSurfaceDrawer {
if (activityInfo == null || activityInfo.packageName == null) {
return;
}
-
- final int displayId = taskInfo.displayId;
- final int taskId = taskInfo.taskId;
-
// replace with the default theme if the application didn't set
final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d",
- activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType);
- final Display display = getDisplay(displayId);
- if (display == null) {
- // Can't show splash screen on requested display, so skip showing at all.
- return;
- }
- Context context = displayId == DEFAULT_DISPLAY
- ? mContext : mContext.createDisplayContext(display);
+ final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
+ suggestType, mDisplayManager);
if (context == null) {
return;
}
- if (theme != context.getThemeResId()) {
- try {
- context = context.createPackageContextAsUser(activityInfo.packageName,
- CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
- context.setTheme(theme);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Failed creating package context with package name "
- + activityInfo.packageName + " for user " + taskInfo.userId, e);
- return;
- }
- }
+ final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
+ context, windowInfo, suggestType, activityInfo.packageName,
+ suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken);
- final Configuration taskConfig = taskInfo.getConfiguration();
- if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "addSplashScreen: creating context based on task Configuration %s",
- taskConfig);
- final Context overrideContext = context.createConfigurationContext(taskConfig);
- overrideContext.setTheme(theme);
- final TypedArray typedArray = overrideContext.obtainStyledAttributes(
- com.android.internal.R.styleable.Window);
- final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
- try {
- if (resId != 0 && overrideContext.getDrawable(resId) != null) {
- // We want to use the windowBackground for the override context if it is
- // available, otherwise we use the default one to make sure a themed starting
- // window is displayed for the app.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "addSplashScreen: apply overrideConfig %s",
- taskConfig);
- context = overrideContext;
- }
- } catch (Resources.NotFoundException e) {
- Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
- + taskId, e);
- return;
- }
- typedArray.recycle();
- }
-
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
- params.setFitInsetsSides(0);
- params.setFitInsetsTypes(0);
- params.format = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
- ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
- int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
- final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
- if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
- windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
- }
- if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
- if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
- windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- }
- } else {
- windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- }
- params.layoutInDisplayCutoutMode = a.getInt(
- R.styleable.Window_windowLayoutInDisplayCutoutMode,
- params.layoutInDisplayCutoutMode);
- params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
- a.recycle();
-
- // Assumes it's safe to show starting windows of launched apps while
- // the keyguard is being hidden. This is okay because starting windows never show
- // secret information.
- // TODO(b/113840485): Occluded may not only happen on default display
- if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
- windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
- }
-
- // Force the window flags: this is a fake window, so it is not really
- // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
- // flag because we do know that the next window will take input
- // focus, so we want to get the IME window up on top of us right away.
- // Touches will only pass through to the host activity window and will be blocked from
- // passing to any other windows.
- windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- params.flags = windowFlags;
- params.token = appToken;
- params.packageName = activityInfo.packageName;
- params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
- if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- }
-
- params.setTitle("Splash Screen " + activityInfo.packageName);
+ final int displayId = taskInfo.displayId;
+ final int taskId = taskInfo.taskId;
+ final Display display = getDisplay(displayId);
// TODO(b/173975965) tracking performance
// Prepare the splash screen content view on splash screen worker thread in parallel, so the
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 7b498e4f54ec..a05ed4f24a08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -18,50 +18,16 @@ package com.android.wm.shell.startingsurface;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
-import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
-import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.getNavigationBarRect;
-
import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityThread;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
-import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -73,19 +39,14 @@ import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.window.ClientWindowFrames;
+import android.window.SnapshotDrawerUtils;
import android.window.StartingWindowInfo;
import android.window.TaskSnapshot;
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DecorView;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.ShellExecutor;
@@ -99,27 +60,8 @@ import java.lang.ref.WeakReference;
* @hide
*/
public class TaskSnapshotWindow {
- /**
- * When creating the starting window, we use the exact same layout flags such that we end up
- * with a window with the exact same dimensions etc. However, these flags are not used in layout
- * and might cause other side effects so we exclude them.
- */
- static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
- | FLAG_NOT_TOUCHABLE
- | FLAG_NOT_TOUCH_MODAL
- | FLAG_ALT_FOCUSABLE_IM
- | FLAG_NOT_FOCUSABLE
- | FLAG_HARDWARE_ACCELERATED
- | FLAG_IGNORE_CHEEK_PRESSES
- | FLAG_LOCAL_FOCUS_MODE
- | FLAG_SLIPPERY
- | FLAG_WATCH_OUTSIDE_TOUCH
- | FLAG_SPLIT_TOUCH
- | FLAG_SCALED
- | FLAG_SECURE;
-
private static final String TAG = StartingWindowController.TAG;
- private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
+ private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=";
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
/**
@@ -132,25 +74,12 @@ public class TaskSnapshotWindow {
private final Window mWindow;
private final Runnable mClearWindowHandler;
private final ShellExecutor mSplashScreenExecutor;
- private final SurfaceControl mSurfaceControl;
private final IWindowSession mSession;
- private final Rect mTaskBounds;
- private final Rect mFrame = new Rect();
- private final Rect mSystemBarInsets = new Rect();
- private TaskSnapshot mSnapshot;
- private final RectF mTmpSnapshotSize = new RectF();
- private final RectF mTmpDstFrame = new RectF();
- private final CharSequence mTitle;
private boolean mHasDrawn;
- private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
private final int mActivityType;
- private final int mStatusBarColor;
- private final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
- private final SurfaceControl.Transaction mTransaction;
- private final Matrix mSnapshotMatrix = new Matrix();
- private final float[] mTmpFloat9 = new float[9];
+
private final Runnable mScheduledRunnable = this::removeImmediately;
private final boolean mHasImeSurface;
@@ -162,42 +91,15 @@ public class TaskSnapshotWindow {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"create taskSnapshot surface for task: %d", taskId);
- final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
- final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
- if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
- Slog.w(TAG, "unable to create taskSnapshot surface for task: " + taskId);
+
+ final WindowManager.LayoutParams layoutParams = SnapshotDrawerUtils.createLayoutParameters(
+ info, TITLE_FORMAT + taskId, TYPE_APPLICATION_STARTING,
+ snapshot.getHardwareBuffer().getFormat(), appToken);
+ if (layoutParams == null) {
+ Slog.e(TAG, "TaskSnapshotWindow no layoutParams");
return null;
}
- final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
-
- final int appearance = attrs.insetsFlags.appearance;
- final int windowFlags = attrs.flags;
- final int windowPrivateFlags = attrs.privateFlags;
-
- layoutParams.packageName = mainWindowParams.packageName;
- layoutParams.windowAnimations = mainWindowParams.windowAnimations;
- layoutParams.dimAmount = mainWindowParams.dimAmount;
- layoutParams.type = TYPE_APPLICATION_STARTING;
- layoutParams.format = snapshot.getHardwareBuffer().getFormat();
- layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
- | FLAG_NOT_FOCUSABLE
- | FLAG_NOT_TOUCHABLE;
- // Setting as trusted overlay to let touches pass through. This is safe because this
- // window is controlled by the system.
- layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
- | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
- layoutParams.token = appToken;
- layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.insetsFlags.appearance = appearance;
- layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
- layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
- layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
- layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
- layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
-
- layoutParams.setTitle(String.format(TITLE_FORMAT, taskId));
final Point taskSize = snapshot.getTaskSize();
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
@@ -209,7 +111,7 @@ public class TaskSnapshotWindow {
final SurfaceControl surfaceControl = new SurfaceControl();
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
- final InsetsSourceControl[] tmpControls = new InsetsSourceControl[0];
+ final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
final TaskDescription taskDescription;
@@ -221,9 +123,8 @@ public class TaskSnapshotWindow {
}
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
- surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
- windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
- topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
+ snapshot, taskDescription, orientation, activityType,
+ clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState tmpInsetsState = new InsetsState();
@@ -233,7 +134,7 @@ public class TaskSnapshotWindow {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- info.requestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls,
+ info.requestedVisibleTypes, tmpInputChannel, tmpInsetsState, tmpControls,
new Rect(), sizeCompatScale);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
@@ -254,33 +155,25 @@ public class TaskSnapshotWindow {
snapshotSurface.clearWindowSynced();
}
- final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState);
- snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
- snapshotSurface.drawSnapshot();
+ SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
+ taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */);
+ snapshotSurface.mHasDrawn = true;
+ snapshotSurface.reportDrawn();
+
return snapshotSurface;
}
- public TaskSnapshotWindow(SurfaceControl surfaceControl,
- TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
- int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
- int currentOrientation, int activityType, InsetsState topWindowInsetsState,
- Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) {
+ public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription,
+ int currentOrientation, int activityType, Runnable clearWindowHandler,
+ ShellExecutor splashScreenExecutor) {
mSplashScreenExecutor = splashScreenExecutor;
mSession = WindowManagerGlobal.getWindowSession();
mWindow = new Window();
mWindow.setSession(mSession);
- mSurfaceControl = surfaceControl;
- mSnapshot = snapshot;
- mTitle = title;
int backgroundColor = taskDescription.getBackgroundColor();
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
- mTaskBounds = taskBounds;
- mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
- windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState);
- mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
mActivityType = activityType;
- mTransaction = new SurfaceControl.Transaction();
mClearWindowHandler = clearWindowHandler;
mHasImeSurface = snapshot.hasImeSurface();
}
@@ -293,23 +186,6 @@ public class TaskSnapshotWindow {
return mHasImeSurface;
}
- /**
- * Ask system bar background painter to draw status bar background.
- * @hide
- */
- public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
- mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
- mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
- }
-
- /**
- * Ask system bar background painter to draw navigation bar background.
- * @hide
- */
- public void drawNavigationBarBackground(Canvas c) {
- mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
- }
-
void scheduleRemove(boolean deferRemoveForIme) {
// Show the latest content as soon as possible for unlocking to home.
if (mActivityType == ACTIVITY_TYPE_HOME) {
@@ -337,178 +213,6 @@ public class TaskSnapshotWindow {
}
/**
- * Set frame size.
- * @hide
- */
- public void setFrames(Rect frame, Rect systemBarInsets) {
- mFrame.set(frame);
- mSystemBarInsets.set(systemBarInsets);
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- mSizeMismatch = (mFrame.width() != snapshot.getWidth()
- || mFrame.height() != snapshot.getHeight());
- mSystemBarBackgroundPainter.setInsets(systemBarInsets);
- }
-
- static Rect getSystemBarInsets(Rect frame, InsetsState state) {
- return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
- false /* ignoreVisibility */).toRect();
- }
-
- private void drawSnapshot() {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "Drawing snapshot surface sizeMismatch=%b", mSizeMismatch);
- if (mSizeMismatch) {
- // The dimensions of the buffer and the window don't match, so attaching the buffer
- // will fail. Better create a child window with the exact dimensions and fill the parent
- // window with the background color!
- drawSizeMismatchSnapshot();
- } else {
- drawSizeMatchSnapshot();
- }
- mHasDrawn = true;
- reportDrawn();
-
- // In case window manager leaks us, make sure we don't retain the snapshot.
- if (mSnapshot.getHardwareBuffer() != null) {
- mSnapshot.getHardwareBuffer().close();
- }
- mSnapshot = null;
- mSurfaceControl.release();
- }
-
- private void drawSizeMatchSnapshot() {
- mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
- .setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
- .apply();
- }
-
- private void drawSizeMismatchSnapshot() {
- final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
- final SurfaceSession session = new SurfaceSession();
-
- // We consider nearly matched dimensions as there can be rounding errors and the user won't
- // notice very minute differences from scaling one dimension more than the other
- final boolean aspectRatioMismatch = Math.abs(
- ((float) buffer.getWidth() / buffer.getHeight())
- - ((float) mFrame.width() / mFrame.height())) > 0.01f;
-
- // Keep a reference to it such that it doesn't get destroyed when finalized.
- SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
- .setName(mTitle + " - task-snapshot-surface")
- .setBLASTLayer()
- .setFormat(buffer.getFormat())
- .setParent(mSurfaceControl)
- .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
- .build();
-
- final Rect frame;
- // We can just show the surface here as it will still be hidden as the parent is
- // still hidden.
- mTransaction.show(childSurfaceControl);
- if (aspectRatioMismatch) {
- // Clip off ugly navigation bar.
- final Rect crop = calculateSnapshotCrop();
- frame = calculateSnapshotFrame(crop);
- mTransaction.setWindowCrop(childSurfaceControl, crop);
- mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
- mTmpSnapshotSize.set(crop);
- mTmpDstFrame.set(frame);
- } else {
- frame = null;
- mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
- mTmpDstFrame.set(mFrame);
- mTmpDstFrame.offsetTo(0, 0);
- }
-
- // Scale the mismatch dimensions to fill the task bounds
- mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
- mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
- mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
- mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
-
- if (aspectRatioMismatch) {
- GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
- PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
- | GraphicBuffer.USAGE_SW_WRITE_RARELY);
- // TODO: Support this on HardwareBuffer
- final Canvas c = background.lockCanvas();
- drawBackgroundAndBars(c, frame);
- background.unlockCanvasAndPost(c);
- mTransaction.setBuffer(mSurfaceControl,
- HardwareBuffer.createFromGraphicBuffer(background));
- }
- mTransaction.apply();
- childSurfaceControl.release();
- }
-
- /**
- * Calculates the snapshot crop in snapshot coordinate space.
- *
- * @return crop rect in snapshot coordinate space.
- */
- public Rect calculateSnapshotCrop() {
- final Rect rect = new Rect();
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
- final Rect insets = mSnapshot.getContentInsets();
-
- final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
- final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
-
- // Let's remove all system decorations except the status bar, but only if the task is at the
- // very top of the screen.
- final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
- rect.inset((int) (insets.left * scaleX),
- isTop ? 0 : (int) (insets.top * scaleY),
- (int) (insets.right * scaleX),
- (int) (insets.bottom * scaleY));
- return rect;
- }
-
- /**
- * Calculates the snapshot frame in window coordinate space from crop.
- *
- * @param crop rect that is in snapshot coordinate space.
- */
- public Rect calculateSnapshotFrame(Rect crop) {
- final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
- final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
- final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
-
- // Rescale the frame from snapshot to window coordinate space
- final Rect frame = new Rect(0, 0,
- (int) (crop.width() / scaleX + 0.5f),
- (int) (crop.height() / scaleY + 0.5f)
- );
-
- // However, we also need to make space for the navigation bar on the left side.
- frame.offset(mSystemBarInsets.left, 0);
- return frame;
- }
-
- /**
- * Draw status bar and navigation bar background.
- * @hide
- */
- public void drawBackgroundAndBars(Canvas c, Rect frame) {
- final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
- final boolean fillHorizontally = c.getWidth() > frame.right;
- final boolean fillVertically = c.getHeight() > frame.bottom;
- if (fillHorizontally) {
- c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
- c.getWidth(), fillVertically
- ? frame.bottom
- : c.getHeight(),
- mBackgroundPaint);
- }
- if (fillVertically) {
- c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
- }
- mSystemBarBackgroundPainter.drawDecors(c, frame);
- }
-
- /**
* Clear window from drawer, must be post on main executor.
*/
private void clearWindowSynced() {
@@ -535,7 +239,7 @@ public class TaskSnapshotWindow {
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int seqId,
- int resizeMode) {
+ boolean dragResizing) {
final TaskSnapshotWindow snapshot = mOuter.get();
if (snapshot == null) {
return;
@@ -556,91 +260,4 @@ public class TaskSnapshotWindow {
});
}
}
-
- /**
- * Helper class to draw the background of the system bars in regions the task snapshot isn't
- * filling the window.
- */
- static class SystemBarBackgroundPainter {
- private final Paint mStatusBarPaint = new Paint();
- private final Paint mNavigationBarPaint = new Paint();
- private final int mStatusBarColor;
- private final int mNavigationBarColor;
- private final int mWindowFlags;
- private final int mWindowPrivateFlags;
- private final float mScale;
- private final InsetsState mInsetsState;
- private final Rect mSystemBarInsets = new Rect();
-
- SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
- TaskDescription taskDescription, float scale, InsetsState insetsState) {
- mWindowFlags = windowFlags;
- mWindowPrivateFlags = windowPrivateFlags;
- mScale = scale;
- final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
- final int semiTransparent = context.getColor(
- R.color.system_bar_background_semi_transparent);
- mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
- semiTransparent, taskDescription.getStatusBarColor(), appearance,
- APPEARANCE_LIGHT_STATUS_BARS,
- taskDescription.getEnsureStatusBarContrastWhenTransparent());
- mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
- FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
- taskDescription.getNavigationBarColor(), appearance,
- APPEARANCE_LIGHT_NAVIGATION_BARS,
- taskDescription.getEnsureNavigationBarContrastWhenTransparent()
- && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
- mStatusBarPaint.setColor(mStatusBarColor);
- mNavigationBarPaint.setColor(mNavigationBarColor);
- mInsetsState = insetsState;
- }
-
- void setInsets(Rect systemBarInsets) {
- mSystemBarInsets.set(systemBarInsets);
- }
-
- int getStatusBarColorViewHeight() {
- final boolean forceBarBackground =
- (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
- return (int) (mSystemBarInsets.top * mScale);
- } else {
- return 0;
- }
- }
-
- private boolean isNavigationBarColorViewVisible() {
- final boolean forceBarBackground =
- (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
- }
-
- void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
- drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
- drawNavigationBarBackground(c);
- }
-
- void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
- int statusBarHeight) {
- if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
- && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
- final int rightInset = (int) (mSystemBarInsets.right * mScale);
- final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
- c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
- }
- }
-
- @VisibleForTesting
- void drawNavigationBarBackground(Canvas c) {
- final Rect navigationBarRect = new Rect();
- getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
- mScale);
- final boolean visible = isNavigationBarColorViewVisible();
- if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
- c.drawRect(navigationBarRect, mNavigationBarPaint);
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java
index 74fe8fbbd5e0..5c455270d9b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.startingsurface.tv;
-import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import android.window.StartingWindowInfo;
@@ -30,6 +30,6 @@ public class TvStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorith
@Override
public int getSuggestedWindowType(StartingWindowInfo windowInfo) {
// For now we want to always show empty splash screens on TV.
- return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
+ return STARTING_WINDOW_TYPE_NONE;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 928e71f8d3a6..618c4465db3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -41,6 +41,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -300,6 +301,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ // check if no-animation and skip animation if so.
+ if (Transitions.isAllNoAnimation(info)) {
+ startTransaction.apply();
+ finishTransaction.apply();
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ return true;
+ }
+
if (mAnimations.containsKey(transition)) {
throw new IllegalStateException("Got a duplicate startAnimation call for "
+ transition);
@@ -395,6 +404,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
+ // The back gesture has animated this change before transition happen, so here we don't
+ // play the animation again.
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ continue;
+ }
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index bdcdb63d2cd6..cc4d268a0000 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -34,4 +34,9 @@ interface IShellTransitions {
* Unregisters a remote transition handler.
*/
oneway void unregisterRemote(in RemoteTransition remoteTransition) = 2;
+
+ /**
+ * Retrieves the apply-token used by transactions in Shell
+ */
+ IBinder getShellApplyToken() = 3;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index b647f43da522..66d0a2aa409b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.transition;
-import static android.hardware.HardwareBuffer.RGBA_8888;
-import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
import static android.util.RotationUtils.deltaRotation;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
@@ -37,8 +35,6 @@ import android.graphics.ColorSpace;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
-import android.media.Image;
-import android.media.ImageReader;
import android.util.Slog;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -46,15 +42,15 @@ import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.window.ScreenCapture;
import android.window.TransitionInfo;
import com.android.internal.R;
+import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* This class handles the rotation animation when the device is rotated.
@@ -144,14 +140,14 @@ class ScreenRotationAnimation {
t.reparent(mScreenshotLayer, mAnimLeash);
mStartLuma = change.getSnapshotLuma();
} else {
- SurfaceControl.LayerCaptureArgs args =
- new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ ScreenCapture.LayerCaptureArgs args =
+ new ScreenCapture.LayerCaptureArgs.Builder(mSurfaceControl)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
.build();
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayers(args);
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureLayers(args);
if (screenshotBuffer == null) {
Slog.w(TAG, "Unable to take screenshot of display");
return;
@@ -172,7 +168,7 @@ class ScreenRotationAnimation {
t.setBuffer(mScreenshotLayer, hardwareBuffer);
t.show(mScreenshotLayer);
if (!isCustomRotate()) {
- mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace);
+ mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, colorSpace);
}
}
@@ -403,93 +399,6 @@ class ScreenRotationAnimation {
mTransactionPool.release(t);
}
- /**
- * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the
- * luminance at the borders of the bitmap
- * @return the average luminance of all the pixels at the borders of the bitmap
- */
- private static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) {
- // Cannot read content from buffer with protected usage.
- if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888
- || hasProtectedContent(hardwareBuffer)) {
- return 0;
- }
-
- ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(),
- hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1);
- ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace);
- Image image = ir.acquireLatestImage();
- if (image == null || image.getPlanes().length == 0) {
- return 0;
- }
-
- Image.Plane plane = image.getPlanes()[0];
- ByteBuffer buffer = plane.getBuffer();
- int width = image.getWidth();
- int height = image.getHeight();
- int pixelStride = plane.getPixelStride();
- int rowStride = plane.getRowStride();
- float[] borderLumas = new float[2 * width + 2 * height];
-
- // Grab the top and bottom borders
- int l = 0;
- for (int x = 0; x < width; x++) {
- borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride);
- borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride);
- }
-
- // Grab the left and right borders
- for (int y = 0; y < height; y++) {
- borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride);
- borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride);
- }
-
- // Cleanup
- ir.close();
-
- // Oh, is this too simple and inefficient for you?
- // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians
- Arrays.sort(borderLumas);
- return borderLumas[borderLumas.length / 2];
- }
-
- /**
- * @return whether the hardwareBuffer passed in is marked as protected.
- */
- private static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) {
- return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT;
- }
-
- private static float getPixelLuminance(ByteBuffer buffer, int x, int y,
- int pixelStride, int rowStride) {
- int offset = y * rowStride + x * pixelStride;
- int pixel = 0;
- pixel |= (buffer.get(offset) & 0xff) << 16; // R
- pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
- pixel |= (buffer.get(offset + 2) & 0xff); // B
- pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
- return Color.valueOf(pixel).luminance();
- }
-
- /**
- * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
- * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace)
- */
- private static float getLumaOfSurfaceControl(Rect bounds, SurfaceControl surfaceControl) {
- if (surfaceControl == null) {
- return 0;
- }
-
- Rect crop = new Rect(0, 0, bounds.width(), bounds.height());
- SurfaceControl.ScreenshotHardwareBuffer buffer =
- SurfaceControl.captureLayers(surfaceControl, crop, 1);
- if (buffer == null) {
- return 0;
- }
-
- return getMedianBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace());
- }
-
private static void applyColor(int startColor, int endColor, float[] rgbFloat,
float fraction, SurfaceControl surface, SurfaceControl.Transaction t) {
final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor,
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 ab792ee122c7..6af81f1eb707 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
@@ -48,6 +48,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.animation.Animation;
import android.view.animation.Transformation;
+import android.window.ScreenCapture;
import android.window.TransitionInfo;
import com.android.internal.R;
@@ -314,8 +315,8 @@ public class TransitionAnimationHelper {
.setBufferSize(extensionRect.width(), extensionRect.height())
.build();
- final SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ final ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(surfaceToExtend)
.setSourceCrop(edgeBounds)
.setFrameScale(1)
.setPixelFormat(PixelFormat.RGBA_8888)
@@ -323,8 +324,8 @@ public class TransitionAnimationHelper {
.setAllowProtected(true)
.setCaptureSecureLayers(true)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
- SurfaceControl.captureLayers(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
+ ScreenCapture.captureLayers(captureArgs);
if (edgeBuffer == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 39fe4559c88f..fc2a828fb263 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
@@ -174,6 +175,9 @@ public class Transitions implements RemoteCallable<Transitions> {
}
private void onInit() {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mOrganizer.shareTransactionQueue();
+ }
mShellController.addExternalInterface(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
this::createExternalInterface, this);
@@ -443,6 +447,34 @@ public class Transitions implements RemoteCallable<Transitions> {
return -1;
}
+ /**
+ * Look through a transition and see if all non-closing changes are no-animation. If so, no
+ * animation should play.
+ */
+ static boolean isAllNoAnimation(TransitionInfo info) {
+ if (isClosingType(info.getType())) {
+ // no-animation is only relevant for launching (open) activities.
+ return false;
+ }
+ boolean hasNoAnimation = false;
+ final int changeSize = info.getChanges().size();
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (isClosingType(change.getMode())) {
+ // ignore closing apps since they are a side-effect of the transition and don't
+ // animate.
+ continue;
+ }
+ if (change.hasFlags(FLAG_NO_ANIMATION)) {
+ hasNoAnimation = true;
+ } else {
+ // at-least one relevant participant *is* animated, so we need to animate.
+ return false;
+ }
+ }
+ return hasNoAnimation;
+ }
+
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
@@ -1016,6 +1048,11 @@ public class Transitions implements RemoteCallable<Transitions> {
transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
+
+ @Override
+ public IBinder getShellApplyToken() {
+ return SurfaceControl.Transaction.getDefaultApplyToken();
+ }
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index d3f1332f6224..bb671454e938 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -105,6 +105,7 @@ class DragResizeInputListener implements AutoCloseable {
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
TYPE_APPLICATION,
+ null /* windowToken */,
mFocusGrantToken,
TAG + " of " + decorationSurface.toString(),
mInputChannel);
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 3ca5b9c38aff..d6adaa7d533b 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -48,6 +48,6 @@ android_test {
"wm-flicker-common-assertions",
"wm-flicker-common-app-helpers",
"platform-test-annotations",
- "wmshell-flicker-test-components",
+ "flickertestapplib",
],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 574a9f4da627..27fc381a10d1 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -13,13 +13,19 @@
<option name="run-command" value="cmd window tracing level all" />
<!-- set WM tracing to frame (avoid incomplete states) -->
<option name="run-command" value="cmd window tracing frame" />
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug" />
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false" />
<!-- restart launcher to activate TAPL -->
<option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
+ <!-- Ensure output directory is empty at the start -->
+ <option name="run-command" value="rm -rf /sdcard/flicker" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="WMShellFlickerTests.apk"/>
- <option name="test-file-name" value="WMShellFlickerTestApp.apk" />
+ <option name="test-file-name" value="FlickerTestApp.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.wm.shell.flicker"/>
@@ -32,4 +38,4 @@
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
-</configuration> \ No newline at end of file
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
new file mode 100644
index 000000000000..122c18d41dee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.flicker
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.junit.FlickerBuilderProvider
+import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import org.junit.Assume
+import org.junit.Test
+
+/**
+ * Base test class containing common assertions for [ComponentNameMatcher.NAV_BAR],
+ * [ComponentNameMatcher.TASK_BAR], [ComponentNameMatcher.STATUS_BAR], and general assertions
+ * (layers visible in consecutive states, entire screen covered, etc.)
+ */
+abstract class BaseTest
+@JvmOverloads
+constructor(
+ protected val flicker: FlickerTest,
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
+) {
+ /** Specification of the test transition to execute */
+ abstract val transition: FlickerBuilder.() -> Unit
+
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache flicker
+ * executions
+ */
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup { flicker.scenario.setIsTablet(tapl.isTablet) }
+ transition()
+ }
+ }
+
+ /** Checks that all parts of the screen are covered during the transition */
+ @Presubmit @Test open fun entireScreenCovered() = flicker.entireScreenCovered()
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
+ */
+ @Presubmit
+ @Test
+ open fun navBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun navBarLayerPositionAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerPositionAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition
+ */
+ @Presubmit
+ @Test
+ open fun taskBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
+ @Test
+ open fun taskBarWindowIsAlwaysVisible() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd()
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible()
+
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() }
+ }
+
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index cba396a82a87..51869140e8fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -15,27 +15,26 @@
*/
@file:JvmName("CommonAssertions")
+
package com.android.wm.shell.flicker
-import android.view.Surface
-import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
+import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.common.region.Region
+import com.android.server.wm.traces.common.service.PlatformConsts
-fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
- assertLayersEnd {
- this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
- }
+fun FlickerTest.appPairsDividerIsVisibleAtEnd() {
+ assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() {
- assertLayersEnd {
- this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
- }
+fun FlickerTest.appPairsDividerIsInvisibleAtEnd() {
+ assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTestParameter.appPairsDividerBecomesVisible() {
+fun FlickerTest.appPairsDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -43,91 +42,311 @@ fun FlickerTestParameter.appPairsDividerBecomesVisible() {
}
}
-fun FlickerTestParameter.splitScreenDividerBecomesVisible() {
- layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+fun FlickerTest.splitScreenEntered(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ fromOtherApp: Boolean,
+ appExistAtStart: Boolean = true
+) {
+ if (fromOtherApp) {
+ if (appExistAtStart) {
+ appWindowIsInvisibleAtStart(component1)
+ } else {
+ appWindowIsNotContainAtStart(component1)
+ }
+ } else {
+ appWindowIsVisibleAtStart(component1)
+ }
+ if (appExistAtStart) {
+ appWindowIsInvisibleAtStart(component2)
+ } else {
+ appWindowIsNotContainAtStart(component2)
+ }
+ splitScreenDividerIsInvisibleAtStart()
+
+ appWindowIsVisibleAtEnd(component1)
+ appWindowIsVisibleAtEnd(component2)
+ splitScreenDividerIsVisibleAtEnd()
}
-fun FlickerTestParameter.layerBecomesVisible(
- component: FlickerComponentName
+fun FlickerTest.splitScreenDismissed(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ toHome: Boolean
) {
+ appWindowIsVisibleAtStart(component1)
+ appWindowIsVisibleAtStart(component2)
+ splitScreenDividerIsVisibleAtStart()
+
+ appWindowIsInvisibleAtEnd(component1)
+ if (toHome) {
+ appWindowIsInvisibleAtEnd(component2)
+ } else {
+ appWindowIsVisibleAtEnd(component2)
+ }
+ splitScreenDividerIsInvisibleAtEnd()
+}
+
+fun FlickerTest.splitScreenDividerIsVisibleAtStart() {
+ assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+}
+
+fun FlickerTest.splitScreenDividerIsVisibleAtEnd() {
+ assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+}
+
+fun FlickerTest.splitScreenDividerIsInvisibleAtStart() {
+ assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+}
+
+fun FlickerTest.splitScreenDividerIsInvisibleAtEnd() {
+ assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+}
+
+fun FlickerTest.splitScreenDividerBecomesVisible() {
+ layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+}
+
+fun FlickerTest.splitScreenDividerBecomesInvisible() {
assertLayers {
- this.isInvisible(component)
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
.then()
- .isVisible(component)
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.layerIsVisibleAtEnd(
- component: FlickerComponentName
+fun FlickerTest.layerBecomesVisible(component: IComponentMatcher) {
+ assertLayers { this.isInvisible(component).then().isVisible(component) }
+}
+
+fun FlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
+ assertLayers { this.isVisible(component).then().isInvisible(component) }
+}
+
+fun FlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
+ assertLayersEnd { this.isVisible(component) }
+}
+
+fun FlickerTest.layerKeepVisible(component: IComponentMatcher) {
+ assertLayers { this.isVisible(component) }
+}
+
+fun FlickerTest.splitAppLayerBoundsBecomesVisible(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
- assertLayersEnd {
- this.isVisible(component)
+ assertLayers {
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ .then()
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ .then()
+ .splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ }
+}
+
+fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
+ assertLayers {
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
+ .then()
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ .then()
+ // TODO(b/245472831): Verify the component should snap to divider.
+ .isVisible(component)
}
}
-fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
- rotation: Int,
- component: FlickerComponentName,
- splitLeftTop: Boolean
+fun FlickerTest.splitAppLayerBoundsBecomesInvisible(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- val dividerRegion = this.last().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- this.isInvisible(component)
+ this.splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
.then()
- .invoke("splitAppLayerBoundsBecomesVisible") {
- it.visibleRegion(component).overlaps(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, rotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, rotation)
- }
- )
- }
+ .isVisible(component, true)
+ .then()
+ .isInvisible(component)
}
}
-fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
- rotation: Int,
- component: FlickerComponentName,
- splitLeftTop: Boolean
+fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayersEnd {
- val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(component).overlaps(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, rotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, rotation)
- }
+ splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ }
+}
+
+fun FlickerTest.splitAppLayerBoundsKeepVisible(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
+) {
+ assertLayers {
+ splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
)
}
}
-fun FlickerTestParameter.appWindowBecomesVisible(
- component: FlickerComponentName
+fun FlickerTest.splitAppLayerBoundsChanges(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
+ assertLayers {
+ if (landscapePosLeft) {
+ this.splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ } else {
+ this.splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ .then()
+ .isInvisible(component)
+ .then()
+ .splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ }
+ }
+}
+
+fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
+ rotation: PlatformConsts.Rotation
+): LayersTraceSubject {
+ return invoke("splitAppLayerBoundsSnapToDivider") {
+ it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation)
+ }
+}
+
+fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
+ rotation: PlatformConsts.Rotation
+): LayerTraceEntrySubject {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return invoke {
+ val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(component)
+ .coversAtMost(
+ if (displayBounds.width > displayBounds.height) {
+ if (landscapePosLeft) {
+ Region.from(
+ 0,
+ 0,
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ displayBounds.bounds.bottom
+ )
+ } else {
+ Region.from(
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ 0,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
+ } else {
+ if (portraitPosTop) {
+ Region.from(
+ 0,
+ 0,
+ displayBounds.bounds.right,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2
+ )
+ } else {
+ Region.from(
+ 0,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
+ }
+ )
+ }
+}
+
+fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
assertWm {
this.isAppWindowInvisible(component)
.then()
+ .notContains(component, isOptional = true)
+ .then()
+ .isAppWindowInvisible(component, isOptional = true)
+ .then()
.isAppWindowVisible(component)
}
}
-fun FlickerTestParameter.appWindowIsVisibleAtEnd(
- component: FlickerComponentName
-) {
- assertWmEnd {
- this.isAppWindowVisible(component)
- }
+fun FlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
+ assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) }
}
-fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
- assertLayersEnd {
- this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
- }
+fun FlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
+ assertWmStart { this.isAppWindowVisible(component) }
+}
+
+fun FlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
+ assertWmEnd { this.isAppWindowVisible(component) }
}
-fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
+fun FlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
+ assertWmStart { this.isAppWindowInvisible(component) }
+}
+
+fun FlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
+ assertWmEnd { this.isAppWindowInvisible(component) }
+}
+
+fun FlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
+ assertWmStart { this.notContains(component) }
+}
+
+fun FlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
+ assertWm { this.isAppWindowVisible(component) }
+}
+
+fun FlickerTest.dockedStackDividerIsVisibleAtEnd() {
+ assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) }
+}
+
+fun FlickerTest.dockedStackDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -135,7 +354,7 @@ fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
}
}
-fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
+fun FlickerTest.dockedStackDividerBecomesInvisible() {
assertLayers {
this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -143,105 +362,83 @@ fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
}
}
-fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() {
- assertLayersEnd {
- this.notContains(DOCKED_STACK_DIVIDER_COMPONENT)
- }
+fun FlickerTest.dockedStackDividerNotExistsAtEnd() {
+ assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) }
}
-fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
- rotation: Int,
- primaryComponent: FlickerComponentName
+fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
+ rotation: PlatformConsts.Rotation,
+ primaryComponent: IComponentMatcher
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(primaryComponent)
- .overlaps(getPrimaryRegion(dividerRegion, rotation))
+ visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
- rotation: Int,
- primaryComponent: FlickerComponentName
+fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
+ rotation: PlatformConsts.Rotation,
+ primaryComponent: IComponentMatcher
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(primaryComponent)
- .overlaps(getPrimaryRegion(dividerRegion, rotation))
+ visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
- rotation: Int,
- secondaryComponent: FlickerComponentName
+fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
+ rotation: PlatformConsts.Rotation,
+ secondaryComponent: IComponentMatcher
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(secondaryComponent)
- .overlaps(getSecondaryRegion(dividerRegion, rotation))
+ visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
- rotation: Int,
- secondaryComponent: FlickerComponentName
+fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
+ rotation: PlatformConsts.Rotation,
+ secondaryComponent: IComponentMatcher
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(secondaryComponent)
- .overlaps(getSecondaryRegion(dividerRegion, rotation))
+ visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
}
}
-fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
+fun getPrimaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region.from(
- 0, 0, displayBounds.bounds.right,
- dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset
- )
- } else {
+ return if (rotation.isRotated()) {
Region.from(
- 0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
+ 0,
+ 0,
+ dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
displayBounds.bounds.bottom
)
- }
-}
-
-fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region.from(
- 0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.right, displayBounds.bounds.bottom
- )
} else {
Region.from(
- dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
- displayBounds.bounds.right, displayBounds.bounds.bottom
+ 0,
+ 0,
+ displayBounds.bounds.right,
+ dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset
)
}
}
-fun getSplitLeftTopRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
- Region.from(0, 0, dividerRegion.bounds.left, displayBounds.bounds.bottom)
- } else {
- Region.from(0, 0, displayBounds.bounds.right, dividerRegion.bounds.top)
- }
-}
-
-fun getSplitRightBottomRegion(dividerRegion: Region, rotation: Int): Region {
+fun getSecondaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
+ return if (rotation.isRotated()) {
Region.from(
- dividerRegion.bounds.right, 0, displayBounds.bounds.right,
+ dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset,
+ 0,
+ displayBounds.bounds.right,
displayBounds.bounds.bottom
)
} else {
Region.from(
- 0, dividerRegion.bounds.bottom, displayBounds.bounds.right,
+ 0,
+ dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.right,
displayBounds.bounds.bottom
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index f56eb6e783aa..651d9356d9ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -15,11 +15,21 @@
*/
@file:JvmName("CommonConstants")
+
package com.android.wm.shell.flicker
-import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.ComponentNameMatcher
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
-val SPLIT_SCREEN_DIVIDER_COMPONENT = FlickerComponentName("", "StageCoordinatorSplitDivider#")
+const val LAUNCHER_UI_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentNameMatcher("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentNameMatcher("", "DockedStackDivider#")
+val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentNameMatcher("", "StageCoordinatorSplitDivider#")
+val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
+
+enum class Direction {
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt
new file mode 100644
index 000000000000..87b94ff8668b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.app.Instrumentation
+import android.content.Context
+import android.provider.Settings
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil
+import java.io.IOException
+
+object MultiWindowUtils {
+ private fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e(MultiWindowUtils::class.simpleName, "executeShellCommand error! $e")
+ }
+ }
+
+ fun getDevEnableNonResizableMultiWindow(context: Context): Int =
+ Settings.Global.getInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW
+ )
+
+ fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
+ Settings.Global.putInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ configValue
+ )
+
+ fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
+ executeShellCommand(
+ instrumentation,
+ createConfigSupportsNonResizableMultiWindowCommand(configValue)
+ )
+
+ fun resetMultiWindowConfig(instrumentation: Instrumentation) =
+ executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
+
+ private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
+ "wm set-multi-window-config --supportsNonResizable $configValue"
+
+ private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
index 51f7a18f60dd..e0ef92457f58 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
@@ -51,7 +51,7 @@ class NotificationListener : NotificationListenerService() {
private const val CMD_NOTIFICATION_ALLOW_LISTENER = "cmd notification allow_listener %s"
private const val CMD_NOTIFICATION_DISALLOW_LISTENER =
- "cmd notification disallow_listener %s"
+ "cmd notification disallow_listener %s"
private const val COMPONENT_NAME = "com.android.wm.shell.flicker/.NotificationListener"
private var instance: NotificationListener? = null
@@ -79,25 +79,23 @@ class NotificationListener : NotificationListenerService() {
): StatusBarNotification? {
instance?.run {
return notifications.values.firstOrNull(predicate)
- } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ }
+ ?: throw IllegalStateException("NotificationListenerService is not connected")
}
fun waitForNotificationToAppear(
predicate: (StatusBarNotification) -> Boolean
): StatusBarNotification? {
instance?.let {
- return waitForResult(extractor = {
- it.notifications.values.firstOrNull(predicate)
- }).second
- } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ return waitForResult(extractor = { it.notifications.values.firstOrNull(predicate) })
+ .second
+ }
+ ?: throw IllegalStateException("NotificationListenerService is not connected")
}
- fun waitForNotificationToDisappear(
- predicate: (StatusBarNotification) -> Boolean
- ): Boolean {
- return instance?.let {
- wait { it.notifications.values.none(predicate) }
- } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean {
+ return instance?.let { wait { it.notifications.values.none(predicate) } }
+ ?: throw IllegalStateException("NotificationListenerService is not connected")
}
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
index 4d87ec9e872f..556cb06f3ca1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
@@ -15,6 +15,7 @@
*/
@file:JvmName("WaitUtils")
+
package com.android.wm.shell.flicker
import android.os.SystemClock
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 278ba9b0f4db..996b677470fe 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
@@ -17,40 +17,38 @@
package com.android.wm.shell.flicker.bubble
import android.app.INotificationManager
-import android.app.Instrumentation
import android.app.NotificationManager
import android.content.Context
+import android.content.pm.PackageManager
import android.os.ServiceManager
-import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.IFlickerTestData
+import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
-import com.android.wm.shell.flicker.helpers.LaunchBubbleHelper
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
-/**
- * Base configurations for Bubble flicker tests
- */
-abstract class BaseBubbleScreen(protected val testSpec: FlickerTestParameter) {
+/** Base configurations for Bubble flicker tests */
+abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
protected val testApp = LaunchBubbleHelper(instrumentation)
- protected val notifyManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE))
-
- protected val uid = context.packageManager.getApplicationInfo(
- testApp.component.packageName, 0).uid
+ private val notifyManager =
+ INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)
+ )
- protected abstract val transition: FlickerBuilder.() -> Unit
+ private val uid =
+ context.packageManager
+ .getApplicationInfo(testApp.`package`, PackageManager.ApplicationInfoFlags.of(0))
+ .uid
@JvmOverloads
protected open fun buildTransition(
@@ -58,46 +56,41 @@ abstract class BaseBubbleScreen(protected val testSpec: FlickerTestParameter) {
): FlickerBuilder.() -> Unit {
return {
setup {
- test {
- notifyManager.setBubblesAllowed(testApp.component.packageName,
- uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
- testApp.launchViaIntent(wmHelper)
- waitAndGetAddBubbleBtn()
- waitAndGetCancelAllBtn()
- }
+ notifyManager.setBubblesAllowed(
+ testApp.`package`,
+ uid,
+ NotificationManager.BUBBLE_PREFERENCE_ALL
+ )
+ testApp.launchViaIntent(wmHelper)
+ waitAndGetAddBubbleBtn()
+ waitAndGetCancelAllBtn()
}
teardown {
- test {
- notifyManager.setBubblesAllowed(testApp.component.packageName,
- uid, NotificationManager.BUBBLE_PREFERENCE_NONE)
- testApp.exit()
- }
+ notifyManager.setBubblesAllowed(
+ testApp.`package`,
+ uid,
+ NotificationManager.BUBBLE_PREFERENCE_NONE
+ )
+ testApp.exit()
}
extraSpec(this)
}
}
- protected fun Flicker.waitAndGetAddBubbleBtn(): UiObject2? = device.wait(Until.findObject(
- By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
- protected fun Flicker.waitAndGetCancelAllBtn(): UiObject2? = device.wait(Until.findObject(
- By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition(this)
- }
- }
+ protected fun IFlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? =
+ device.wait(Until.findObject(By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
+ protected fun IFlickerTestData.waitAndGetCancelAllBtn(): UiObject2? =
+ device.wait(Until.findObject(By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
const val FIND_OBJECT_TIMEOUT = 2000L
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
index b137e92881a5..7fc12f06f530 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -18,18 +18,18 @@ package com.android.wm.shell.flicker.bubble
import android.content.Context
import android.graphics.Point
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import org.junit.runner.RunWith
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
+import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/**
@@ -38,30 +38,33 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:DismissBubbleScreen`
*
* Actions:
+ * ```
* Dismiss a bubble notification
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-open class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+open class DismissBubbleScreen(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val displaySize = DisplayMetrics()
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- eachRun {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Add Bubble not found")
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Add Bubble not found")
}
transitions {
- wm.run { wm.getDefaultDisplay().getMetrics(displaySize) }
+ wm.run { wm.defaultDisplay.getMetrics(displaySize) }
val dist = Point((displaySize.widthPixels / 2), displaySize.heightPixels)
- val showBubble = device.wait(Until.findObject(
- By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
+ val showBubble =
+ device.wait(
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
showBubble?.run { drag(dist, 1000) } ?: error("Show bubble not found")
}
}
@@ -69,8 +72,22 @@ open class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScree
@Presubmit
@Test
open fun testAppIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
+ flicker.assertLayers { this.isVisible(testApp) }
}
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index f288b0a24d9d..0cda626f8286 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -20,12 +20,11 @@ import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import org.junit.runner.RunWith
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
+import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/**
@@ -34,27 +33,30 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen`
*
* Actions:
+ * ```
* Launch an app and enable app's bubble notification
* Send a bubble notification
* The activity for the bubble is launched
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-open class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+open class ExpandBubbleScreen(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- test {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Add Bubble not found")
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Add Bubble not found")
}
transitions {
- val showBubble = device.wait(Until.findObject(
- By.res("com.android.systemui", "bubble_view")), FIND_OBJECT_TIMEOUT)
+ val showBubble =
+ device.wait(
+ Until.findObject(By.res("com.android.systemui", "bubble_view")),
+ FIND_OBJECT_TIMEOUT
+ )
showBubble?.run { showBubble.click() } ?: error("Bubble notify not found")
}
}
@@ -62,8 +64,6 @@ open class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
@Presubmit
@Test
open fun testAppIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
+ flicker.assertLayers { this.isVisible(testApp) }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 684e5cad0e67..08ed91b3cab1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -16,21 +16,21 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Postsubmit
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
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import org.junit.Assume
-import org.junit.runner.RunWith
import org.junit.Test
+import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/**
@@ -39,44 +39,48 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:LaunchBubbleFromLockScreen`
*
* Actions:
+ * ```
* Launch an bubble from notification on lock screen
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+class LaunchBubbleFromLockScreen(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- eachRun {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Bubble widget not found")
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- device.wakeUp()
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Bubble widget not found")
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
+ device.wakeUp()
}
transitions {
// Swipe & wait for the notification shade to expand so all can be seen
- val wm = context.getSystemService(WindowManager::class.java)
- val metricInsets = wm.getCurrentWindowMetrics().windowInsets
- val insets = metricInsets.getInsetsIgnoringVisibility(
- WindowInsets.Type.statusBars()
- or WindowInsets.Type.displayCutout())
- device.swipe(100, insets.top + 100, 100, device.getDisplayHeight() / 2, 4)
+ val wm =
+ context.getSystemService(WindowManager::class.java)
+ ?: error("Unable to obtain WM service")
+ val metricInsets = wm.currentWindowMetrics.windowInsets
+ val insets =
+ metricInsets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
+ )
+ device.swipe(100, insets.top + 100, 100, device.displayHeight / 2, 4)
device.waitForIdle(2000)
instrumentation.uiAutomation.syncInputTransactions()
- val notification = device.wait(Until.findObject(
- By.text("BubbleChat")), FIND_OBJECT_TIMEOUT)
+ val notification =
+ device.wait(Until.findObject(By.text("BubbleChat")), FIND_OBJECT_TIMEOUT)
notification?.click() ?: error("Notification not found")
instrumentation.uiAutomation.syncInputTransactions()
- val showBubble = device.wait(Until.findObject(
- By.res("com.android.systemui", "bubble_view")), FIND_OBJECT_TIMEOUT)
+ val showBubble =
+ device.wait(
+ Until.findObject(By.res("com.android.systemui", "bubble_view")),
+ FIND_OBJECT_TIMEOUT
+ )
showBubble?.click() ?: error("Bubble notify not found")
instrumentation.uiAutomation.syncInputTransactions()
val cancelAllBtn = waitAndGetCancelAllBtn()
@@ -84,21 +88,81 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr
}
}
- @Presubmit
+ @FlakyTest(bugId = 242088970)
@Test
fun testAppIsVisibleAtEnd() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- testSpec.assertLayersEnd {
- this.isVisible(testApp.component)
- }
+ flicker.assertLayersEnd { this.isVisible(testApp) }
}
+ @Postsubmit @Test fun navBarLayerIsVisibleAtEnd() = flicker.navBarLayerIsVisibleAtEnd()
+
+ @Postsubmit @Test fun navBarLayerPositionAtEnd() = flicker.navBarLayerPositionAtEnd()
+
+ /** {@inheritDoc} */
@FlakyTest
@Test
- fun testAppIsVisibleAtEnd_ShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- testSpec.assertLayersEnd {
- this.isVisible(testApp.component)
- }
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 242088970)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 251217773)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index 0bb4d398bff4..b69ff6451d1c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,12 +16,12 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -32,28 +32,33 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen`
*
* Actions:
+ * ```
* Launch an app and enable app's bubble notification
* Send a bubble notification
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-open class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+open class LaunchBubbleScreen(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
transitions {
val addBubbleBtn = waitAndGetAddBubbleBtn()
addBubbleBtn?.click() ?: error("Bubble widget not found")
+
+ device.wait(
+ Until.findObjects(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
+ ?: error("No bubbles found")
}
}
- @Presubmit
@Test
open fun testAppIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
+ flicker.assertLayers { this.isVisible(testApp) }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index 8d1e315e2d5e..b3a2ad309b27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -20,16 +20,16 @@ import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
-import org.junit.runner.RunWith
import org.junit.Test
+import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/**
@@ -38,38 +38,47 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:MultiBubblesScreen`
*
* Actions:
+ * ```
* Switch in different bubble notifications
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+open class MultiBubblesScreen(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
@Before
open fun before() {
Assume.assumeFalse(isShellTransitionsEnabled)
}
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- test {
- for (i in 1..3) {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
- }
- val showBubble = device.wait(Until.findObject(
- By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
- showBubble?.run { showBubble.click() } ?: error("Show bubble not found")
+ for (i in 1..3) {
+ val addBubbleBtn = waitAndGetAddBubbleBtn() ?: error("Add Bubble not found")
+ addBubbleBtn.click()
SystemClock.sleep(1000)
}
+ val showBubble =
+ device.wait(
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
+ ?: error("Show bubble not found")
+ showBubble.click()
+ SystemClock.sleep(1000)
}
transitions {
- val bubbles = device.wait(Until.findObjects(
- By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
+ val bubbles: List<UiObject2> =
+ device.wait(
+ Until.findObjects(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
+ ?: error("No bubbles found")
for (entry in bubbles) {
- entry?.run { entry.click() } ?: error("Bubble not found")
+ entry.click()
SystemClock.sleep(1000)
}
}
@@ -78,8 +87,6 @@ open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
@Presubmit
@Test
open fun testAppIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
+ flicker.assertLayers { this.isVisible(testApp) }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt
index ddebb6fed636..191f4fa893f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt
@@ -16,12 +16,11 @@
package com.android.wm.shell.flicker.bubble
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.runner.RunWith
@@ -30,13 +29,10 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
@FlakyTest(bugId = 217777115)
-class MultiBubblesScreenShellTransit(
- testSpec: FlickerTestParameter
-) : MultiBubblesScreen(testSpec) {
+class MultiBubblesScreenShellTransit(flicker: FlickerTest) : MultiBubblesScreen(flicker) {
@Before
override fun before() {
Assume.assumeTrue(isShellTransitionsEnabled)
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
deleted file mode 100644
index 41cd31aabf05..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.region.Region
-
-class AppPairsHelper(
- instrumentation: Instrumentation,
- activityLabel: String,
- component: FlickerComponentName
-) : BaseAppHelper(instrumentation, activityLabel, component) {
- fun getPrimaryBounds(dividerBounds: Region): Region {
- val primaryAppBounds = Region.from(0, 0, dividerBounds.bounds.right,
- dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
- return primaryAppBounds
- }
-
- fun getSecondaryBounds(dividerBounds: Region): Region {
- val displayBounds = WindowUtils.displayBounds
- val secondaryAppBounds = Region.from(0,
- dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
- return secondaryAppBounds
- }
-
- companion object {
- const val TEST_REPETITIONS = 1
- const val TIMEOUT_MS = 3_000L
-
- fun Flicker.waitAppsShown(app1: SplitScreenHelper?, app2: SplitScreenHelper?) {
- wmHelper.waitFor("primaryAndSecondaryAppsVisible") { dump ->
- val primaryAppVisible = app1?.let {
- dump.wmState.isWindowSurfaceShown(app1.defaultWindowName)
- } ?: false
- val secondaryAppVisible = app2?.let {
- dump.wmState.isWindowSurfaceShown(app2.defaultWindowName)
- } ?: false
- primaryAppVisible && secondaryAppVisible
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
deleted file mode 100644
index 3dd9e0572947..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import android.content.pm.PackageManager.FEATURE_LEANBACK
-import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
-import android.os.SystemProperties
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import android.util.Log
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiObject2
-import androidx.test.uiautomator.Until
-import com.android.compatibility.common.util.SystemUtil
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.common.FlickerComponentName
-import java.io.IOException
-
-abstract class BaseAppHelper(
- instrumentation: Instrumentation,
- launcherName: String,
- component: FlickerComponentName
-) : StandardAppHelper(
- instrumentation,
- launcherName,
- component,
- LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy
-) {
- private val appSelector = By.pkg(component.packageName).depth(0)
-
- protected val isTelevision: Boolean
- get() = context.packageManager.run {
- hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
- }
-
- val defaultWindowName: String
- get() = component.toWindowName()
-
- val ui: UiObject2?
- get() = uiDevice.findObject(appSelector)
-
- fun waitUntilClosed(): Boolean {
- return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
- }
-
- companion object {
- private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
-
- fun isShellTransitionsEnabled() =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false)
-
- fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
- try {
- SystemUtil.runShellCommand(instrumentation, cmd)
- } catch (e: IOException) {
- Log.e("BaseAppHelper", "executeShellCommand error! $e")
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
deleted file mode 100644
index 471e010cf560..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
-
-class FixedAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.FixedActivity.LABEL,
- Components.FixedActivity.COMPONENT.toFlickerComponent()
-) \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
deleted file mode 100644
index cc5b9f9eb26d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.testapp.Components
-
-open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT.toFlickerComponent()
-) {
- /**
- * Opens the IME and wait for it to be displayed
- *
- * @param wmHelper Helper used to wait for WindowManager states
- */
- @JvmOverloads
- open fun openIME(wmHelper: WindowManagerStateHelper? = null) {
- if (!isTelevision) {
- val editText = uiDevice.wait(
- Until.findObject(By.res(getPackage(), "plain_text_input")),
- FIND_TIMEOUT)
-
- require(editText != null) {
- "Text field not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
- }
- editText.click()
- waitAndAssertIMEShown(uiDevice, wmHelper)
- } else {
- // If we do the same thing as above - editText.click() - on TV, that's going to force TV
- // into the touch mode. We really don't want that.
- launchViaIntent(action = Components.ImeActivity.ACTION_OPEN_IME)
- }
- }
-
- protected fun waitAndAssertIMEShown(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper? = null
- ) {
- if (wmHelper == null) {
- device.waitForIdle()
- } else {
- wmHelper.waitImeShown()
- }
- }
-
- /**
- * Opens the IME and wait for it to be gone
- *
- * @param wmHelper Helper used to wait for WindowManager states
- */
- @JvmOverloads
- open fun closeIME(wmHelper: WindowManagerStateHelper? = null) {
- if (!isTelevision) {
- uiDevice.pressBack()
- // Using only the AccessibilityInfo it is not possible to identify if the IME is active
- if (wmHelper == null) {
- uiDevice.waitForIdle()
- } else {
- wmHelper.waitImeGone()
- }
- } else {
- // While pressing the back button should close the IME on TV as well, it may also lead
- // to the app closing. So let's instead just ask the app to close the IME.
- launchViaIntent(action = Components.ImeActivity.ACTION_CLOSE_IME)
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
deleted file mode 100644
index 6695c17ed514..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
-
-class LaunchBubbleHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.LaunchBubbleActivity.LABEL,
- Components.LaunchBubbleActivity.COMPONENT.toFlickerComponent()
-) {
-
- companion object {
- const val TEST_REPETITIONS = 1
- const val TIMEOUT_MS = 3_000L
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
deleted file mode 100644
index 12ccbafce651..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import android.content.Context
-import android.provider.Settings
-import com.android.server.wm.traces.common.FlickerComponentName
-
-class MultiWindowHelper(
- instrumentation: Instrumentation,
- activityLabel: String,
- componentsInfo: FlickerComponentName
-) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
-
- companion object {
- fun getDevEnableNonResizableMultiWindow(context: Context): Int =
- Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
-
- fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue)
-
- fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
- executeShellCommand(
- instrumentation,
- createConfigSupportsNonResizableMultiWindowCommand(configValue))
-
- fun resetMultiWindowConfig(instrumentation: Instrumentation) =
- executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
-
- private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
- "wm set-multi-window-config --supportsNonResizable $configValue"
-
- private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
deleted file mode 100644
index 8157a4e453af..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import android.media.session.MediaController
-import android.media.session.MediaSessionManager
-import android.os.SystemClock
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
-import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
-import com.android.server.wm.traces.common.Rect
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
-import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
-import com.android.wm.shell.flicker.testapp.Components
-
-class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.PipActivity.LABEL,
- Components.PipActivity.COMPONENT.toFlickerComponent()
-) {
- private val mediaSessionManager: MediaSessionManager
- get() = context.getSystemService(MediaSessionManager::class.java)
- ?: error("Could not get MediaSessionManager")
-
- private val mediaController: MediaController?
- get() = mediaSessionManager.getActiveSessions(null).firstOrNull {
- it.packageName == component.packageName
- }
-
- fun clickObject(resId: String) {
- val selector = By.res(component.packageName, resId)
- val obj = uiDevice.findObject(selector) ?: error("Could not find `$resId` object")
-
- if (!isTelevision) {
- obj.click()
- } else {
- focusOnObject(selector) || error("Could not focus on `$resId` object")
- uiDevice.pressDPadCenter()
- }
- }
-
- /**
- * Launches the app through an intent instead of interacting with the launcher and waits
- * until the app window is in PIP mode
- */
- @JvmOverloads
- fun launchViaIntentAndWaitForPip(
- wmHelper: WindowManagerStateHelper,
- expectedWindowName: String = "",
- action: String? = null,
- stringExtras: Map<String, String>
- ) {
- launchViaIntentAndWaitShown(
- wmHelper, expectedWindowName, action, stringExtras,
- waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition)
- )
- }
-
- /**
- * Expand the PIP window back to full screen via intent and wait until the app is visible
- */
- fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
- launchViaIntentAndWaitShown(wmHelper)
-
- private fun focusOnObject(selector: BySelector): Boolean {
- // We expect all the focusable UI elements to be arranged in a way so that it is possible
- // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
- // from "the bottom".
- repeat(FOCUS_ATTEMPTS) {
- uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true }
- ?: error("The object we try to focus on is gone.")
-
- uiDevice.pressDPadDown()
- uiDevice.waitForIdle()
- }
- return false
- }
-
- @JvmOverloads
- fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) {
- clickObject(ENTER_PIP_BUTTON_ID)
-
- // Wait on WMHelper or simply wait for 3 seconds
- wmHelper?.waitPipShown() ?: SystemClock.sleep(3_000)
- // when entering pip, the dismiss button is visible at the start. to ensure the pip
- // animation is complete, wait until the pip dismiss button is no longer visible.
- // b/176822698: dismiss-only state will be removed in the future
- uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
- }
-
- fun enableEnterPipOnUserLeaveHint() {
- clickObject(ENTER_PIP_ON_USER_LEAVE_HINT)
- }
-
- fun enableAutoEnterForPipActivity() {
- clickObject(ENTER_PIP_AUTOENTER)
- }
-
- fun clickStartMediaSessionButton() {
- clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
- }
-
- fun checkWithCustomActionsCheckbox() = uiDevice
- .findObject(By.res(component.packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
- ?.takeIf { it.isCheckable }
- ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
- ?: error("'With custom actions' checkbox not found")
-
- fun pauseMedia() = mediaController?.transportControls?.pause()
- ?: error("No active media session found")
-
- fun stopMedia() = mediaController?.transportControls?.stop()
- ?: error("No active media session found")
-
- @Deprecated(
- "Use PipAppHelper.closePipWindow(wmHelper) instead",
- ReplaceWith("closePipWindow(wmHelper)")
- )
- fun closePipWindow() {
- if (isTelevision) {
- uiDevice.closeTvPipWindow()
- } else {
- closePipWindow(WindowManagerStateHelper(mInstrumentation))
- }
- }
-
- private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
- val windowRegion = wmHelper.getWindowRegion(component)
- require(!windowRegion.isEmpty) {
- "Unable to find a PIP window in the current state"
- }
- return windowRegion.bounds
- }
-
- /**
- * Taps the pip window and dismisses it by clicking on the X button.
- */
- fun closePipWindow(wmHelper: WindowManagerStateHelper) {
- if (isTelevision) {
- uiDevice.closeTvPipWindow()
- } else {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the dismiss button
- val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
- uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
- val dismissPipObject = uiDevice.findObject(dismissSelector)
- ?: error("PIP window dismiss button not found")
- val dismissButtonBounds = dismissPipObject.visibleBounds
- uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
- }
-
- // Wait for animation to complete.
- wmHelper.waitPipGone()
- wmHelper.waitForHomeActivityVisible()
- }
-
- /**
- * Close the pip window by pressing the expand button
- */
- fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the expand button
- val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
- uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
- val expandPipObject = uiDevice.findObject(expandSelector)
- ?: error("PIP window expand button not found")
- val expandButtonBounds = expandPipObject.visibleBounds
- uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
- wmHelper.waitPipGone()
- wmHelper.waitForAppTransitionIdle()
- }
-
- /**
- * Double click on the PIP window to expand it
- */
- fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- wmHelper.waitForAppTransitionIdle()
- }
-
- companion object {
- private const val FOCUS_ATTEMPTS = 20
- private const val ENTER_PIP_BUTTON_ID = "enter_pip"
- private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions"
- private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
- private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
- private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
deleted file mode 100644
index 4d0fbc4a0e38..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
-
-class SimpleAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.SimpleActivity.LABEL,
- Components.SimpleActivity.COMPONENT.toFlickerComponent()
-) \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
deleted file mode 100644
index 49eca63a23ec..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import android.graphics.Point
-import android.os.SystemClock
-import android.view.InputDevice
-import android.view.MotionEvent
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.Until
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assert
-
-class SplitScreenHelper(
- instrumentation: Instrumentation,
- activityLabel: String,
- componentsInfo: FlickerComponentName
-) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
-
- companion object {
- const val TEST_REPETITIONS = 1
- const val TIMEOUT_MS = 3_000L
- const val DRAG_DURATION_MS = 1_000L
- const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
- const val GESTURE_STEP_MS = 16L
-
- private val notificationScrollerSelector: BySelector
- get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
- private val notificationContentSelector: BySelector
- get() = By.text("Notification content")
-
- fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getSendNotification(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SendNotificationActivity.LABEL,
- Components.SendNotificationActivity.COMPONENT.toFlickerComponent()
- )
-
- fun dragFromNotificationToSplit(
- instrumentation: Instrumentation,
- device: UiDevice,
- wmHelper: WindowManagerStateHelper
- ) {
- val displayBounds = wmHelper.currentState.layerState
- .displays.firstOrNull { !it.isVirtual }
- ?.layerStackSpace
- ?: error("Display not found")
-
- // Pull down the notifications
- device.swipe(
- displayBounds.centerX(), 5,
- displayBounds.centerX(), displayBounds.bottom, 20 /* steps */
- )
- SystemClock.sleep(TIMEOUT_MS)
-
- // Find the target notification
- val notificationScroller = device.wait(
- Until.findObject(notificationScrollerSelector), TIMEOUT_MS
- )
- var notificationContent = notificationScroller.findObject(notificationContentSelector)
-
- while (notificationContent == null) {
- device.swipe(
- displayBounds.centerX(), displayBounds.centerY(),
- displayBounds.centerX(), displayBounds.centerY() - 150, 20 /* steps */
- )
- notificationContent = notificationScroller.findObject(notificationContentSelector)
- }
-
- // Drag to split
- var dragStart = notificationContent.visibleCenter
- var dragMiddle = Point(dragStart.x + 50, dragStart.y)
- var dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
- val downTime = SystemClock.uptimeMillis()
-
- touch(
- instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime,
- TIMEOUT_MS, dragStart
- )
- // It needs a horizontal movement to trigger the drag
- touchMove(
- instrumentation, downTime, SystemClock.uptimeMillis(),
- DRAG_DURATION_MS, dragStart, dragMiddle
- )
- touchMove(
- instrumentation, downTime, SystemClock.uptimeMillis(),
- DRAG_DURATION_MS, dragMiddle, dragEnd
- )
- // Wait for a while to start splitting
- SystemClock.sleep(TIMEOUT_MS)
- touch(
- instrumentation, MotionEvent.ACTION_UP, downTime, SystemClock.uptimeMillis(),
- GESTURE_STEP_MS, dragEnd
- )
- SystemClock.sleep(TIMEOUT_MS)
- }
-
- fun touch(
- instrumentation: Instrumentation,
- action: Int,
- downTime: Long,
- eventTime: Long,
- duration: Long,
- point: Point
- ) {
- val motionEvent = MotionEvent.obtain(
- downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0
- )
- motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
- instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
- motionEvent.recycle()
- SystemClock.sleep(duration)
- }
-
- fun touchMove(
- instrumentation: Instrumentation,
- downTime: Long,
- eventTime: Long,
- duration: Long,
- from: Point,
- to: Point
- ) {
- val steps: Long = duration / GESTURE_STEP_MS
- var currentTime = eventTime
- var currentX = from.x.toFloat()
- var currentY = from.y.toFloat()
- val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
- val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
-
- for (i in 1..steps) {
- val motionMove = MotionEvent.obtain(
- downTime, currentTime, MotionEvent.ACTION_MOVE, currentX, currentY, 0
- )
- motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
- instrumentation.uiAutomation.injectInputEvent(motionMove, true)
- motionMove.recycle()
-
- currentTime += GESTURE_STEP_MS
- if (i == steps - 1) {
- currentX = to.x.toFloat()
- currentY = to.y.toFloat()
- } else {
- currentX += stepX
- currentY += stepY
- }
- SystemClock.sleep(GESTURE_STEP_MS)
- }
- }
-
- fun createShortcutOnHotseatIfNotExist(
- taplInstrumentation: LauncherInstrumentation,
- appName: String
- ) {
- taplInstrumentation.workspace
- .deleteAppIcon(taplInstrumentation.workspace.getHotseatAppIcon(0))
- val allApps = taplInstrumentation.workspace.switchToAllApps()
- allApps.freeze()
- try {
- val appIconSrc = allApps.getAppIcon(appName)
- Assert.assertNotNull("Unable to find app icon", appIconSrc)
- val appIconDest = appIconSrc.dragToHotseat(0)
- Assert.assertNotNull("Unable to drag app icon on hotseat", appIconDest)
- } finally {
- allApps.unfreeze()
- }
- }
- }
-}
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 ce624f2b5bbe..5e898e8710cd 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
@@ -17,12 +17,16 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,69 +40,65 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
*
* Actions:
+ * ```
* Launch an app in full screen
* Select "Auto-enter PiP" radio button
* Press Home button or swipe up to go Home and put [pipApp] in pip mode
- *
+ * ```
* Notes:
+ * ```
* 1. All assertions are inherited from [EnterPipTest]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
@FlakyTest(bugId = 238367575)
-@Group3
-class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) {
- protected val taplInstrumentation = LauncherInstrumentation()
- /**
- * Defines the transition used to run the test
- */
+class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipTest(flicker) {
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableAutoEnterForPipActivity()
- }
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableAutoEnterForPipActivity()
}
teardown {
- eachRun {
- // close gracefully so that onActivityUnpinned() can be called before force exit
- pipApp.closePipWindow(wmHelper)
- pipApp.exit(wmHelper)
- }
- }
- transitions {
- taplInstrumentation.goHome()
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
+ transitions { tapl.goHome() }
}
+ @FlakyTest(bugId = 256863309)
+ @Test
override fun pipLayerReduces() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
}
}
}
- /**
- * Checks that [pipApp] window is animated towards default position in right bottom corner
- */
+ /** Checks that [pipApp] window is animated towards default position in right bottom corner */
+ @Presubmit
@Test
fun pipLayerMovesTowardsRightBottomCorner() {
// in gestural nav the swipe makes PiP first go upwards
- Assume.assumeFalse(testSpec.isGesturalNavigation)
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
// Pip animates towards the right bottom corner, but because it is being resized at the
// same time, it is possible it shrinks first quickly below the default position and get
// moved up after that in just few last frames
@@ -108,9 +108,11 @@ class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(
}
}
+ @Presubmit
+ @Test
override fun focusChanges() {
// in gestural nav the focus goes to different activity on swipe up
- Assume.assumeFalse(testSpec.isGesturalNavigation)
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
deleted file mode 100644
index f9b08000290f..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("CommonAssertions")
-package com.android.wm.shell.flicker.pip
-
-internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
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 953f59a1f70b..79feeaa3c222 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
@@ -16,14 +16,18 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.traces.common.service.PlatformConsts
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
@@ -34,78 +38,85 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:EnterPipOnUserLeaveHintTest`
*
* Actions:
+ * ```
* Launch an app in full screen
* Select "Via code behind" radio button
* Press Home button or swipe up to go Home and put [pipApp] in pip mode
- *
+ * ```
* Notes:
+ * ```
* 1. All assertions are inherited from [EnterPipTest]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) {
- protected val taplInstrumentation = LauncherInstrumentation()
- /**
- * Defines the transition used to run the test
- */
+class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTest(flicker) {
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableEnterPipOnUserLeaveHint()
- }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableEnterPipOnUserLeaveHint()
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- }
- }
- transitions {
- taplInstrumentation.goHome()
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
+ transitions { tapl.goHome() }
}
+ @Presubmit
+ @Test
override fun pipAppLayerAlwaysVisible() {
- if (!testSpec.isGesturalNavigation) super.pipAppLayerAlwaysVisible() else {
+ if (!flicker.scenario.isGesturalNavigation) super.pipAppLayerAlwaysVisible()
+ else {
// pip layer in gesture nav will disappear during transition
- testSpec.assertLayers {
- this.isVisible(pipApp.component)
- .then().isInvisible(pipApp.component)
- .then().isVisible(pipApp.component)
+ flicker.assertLayers {
+ this.isVisible(pipApp).then().isInvisible(pipApp).then().isVisible(pipApp)
}
}
}
+ @Presubmit
+ @Test
override fun pipLayerReduces() {
// in gestural nav the pip enters through alpha animation
- Assume.assumeFalse(testSpec.isGesturalNavigation)
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.pipLayerReduces()
}
+ @Presubmit
+ @Test
override fun focusChanges() {
// in gestural nav the focus goes to different activity on swipe up
- Assume.assumeFalse(testSpec.isGesturalNavigation)
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
+ @Presubmit
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ @Presubmit
+ @Test
override fun pipLayerRemainInsideVisibleBounds() {
- if (!testSpec.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds() else {
+ if (!flicker.scenario.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds()
+ else {
// pip layer in gesture nav will disappear during transition
- testSpec.assertLayersStart {
- this.visibleRegion(pipApp.component).coversAtMost(displayBounds)
- }
- testSpec.assertLayersEnd {
- this.visibleRegion(pipApp.component).coversAtMost(displayBounds)
- }
+ flicker.assertLayersStart { this.visibleRegion(pipApp).coversAtMost(displayBounds) }
+ flicker.assertLayersEnd { this.visibleRegion(pipApp).coversAtMost(displayBounds) }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 61ac49835185..1a76142330a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -17,14 +17,16 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,65 +39,54 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:EnterPipTest`
*
* Actions:
+ * ```
* Launch an app in full screen
* Press an "enter pip" button to put [pipApp] in pip mode
- *
+ * ```
* 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
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+open class EnterPipTest(flicker: FlickerTest) : PipTransition(flicker) {
- /**
- * Defines the transition used to run the test
- */
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- }
- }
- transitions {
- pipApp.clickEnterPipButton(wmHelper)
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
+ transitions { pipApp.clickEnterPipButton(wmHelper) }
}
- /**
- * Checks [pipApp] window remains visible throughout the animation
- */
+ /** Checks [pipApp] window remains visible throughout the animation */
@Presubmit
@Test
- fun pipAppWindowAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(pipApp.component)
- }
+ open fun pipAppWindowAlwaysVisible() {
+ flicker.assertWm { this.isAppWindowVisible(pipApp) }
}
- /**
- * Checks [pipApp] layer remains visible throughout the animation
- */
+ /** Checks [pipApp] layer remains visible throughout the animation */
@Presubmit
@Test
open fun pipAppLayerAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(pipApp.component)
- }
+ flicker.assertLayers { this.isVisible(pipApp) }
}
/**
@@ -105,9 +96,7 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
@@ -117,79 +106,66 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayersVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
- /**
- * Checks that the visible region of [pipApp] always reduces during the animation
- */
+ /** Checks that the visible region of [pipApp] always reduces during the animation */
@Presubmit
@Test
open fun pipLayerReduces() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
}
}
}
- /**
- * Checks that [pipApp] window becomes pinned
- */
+ /** Checks that [pipApp] window becomes pinned */
@Presubmit
@Test
fun pipWindowBecomesPinned() {
- testSpec.assertWm {
- invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp.component) }
+ flicker.assertWm {
+ invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp) }
.then()
- .invoke("pipWindowIsPinned") { it.isPinned(pipApp.component) }
+ .invoke("pipWindowIsPinned") { it.isPinned(pipApp) }
}
}
- /**
- * Checks [LAUNCHER_COMPONENT] layer remains visible throughout the animation
- */
+ /** Checks [ComponentMatcher.LAUNCHER] layer remains visible throughout the animation */
@Presubmit
@Test
fun launcherLayerBecomesVisible() {
- testSpec.assertLayers {
- isInvisible(LAUNCHER_COMPONENT)
+ flicker.assertLayers {
+ isInvisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isVisible(LAUNCHER_COMPONENT)
+ .isVisible(ComponentNameMatcher.LAUNCHER)
}
}
/**
- * Checks that the focus changes between the [pipApp] window and the launcher when
- * closing the pip window
+ * Checks that the focus changes between the [pipApp] window and the launcher when closing the
+ * pip window
*/
@Presubmit
@Test
open fun focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges(pipApp.`package`, "NexusLauncherActivity")
- }
+ flicker.assertEventLog { this.focusChanges(pipApp.`package`, "NexusLauncherActivity") }
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
+ * and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3
- )
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 7680f4dfa1d5..a4c8d6f11602 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -16,24 +16,29 @@
package com.android.wm.shell.flicker.pip
+import android.app.Activity
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+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.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.service.PlatformConsts
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.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
+import org.junit.Assume
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,71 +51,85 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:EnterPipToOtherOrientationTest`
*
* Actions:
+ * ```
* Launch [testApp] on a fixed portrait orientation
* Launch [pipApp] on a fixed landscape orientation
* Broadcast action [ACTION_ENTER_PIP] to enter pip mode
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class EnterPipToOtherOrientationTest(
- testSpec: FlickerTestParameter
-) : PipTransition(testSpec) {
- private val testApp = FixedAppHelper(instrumentation)
- private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+class EnterPipToOtherOrientationTest(flicker: FlickerTest) : PipTransition(flicker) {
+ private val testApp = FixedOrientationAppHelper(instrumentation)
+ private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90)
+ private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0)
- /**
- * Defines the transition used to run the test
- */
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
-
setup {
- eachRun {
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
+ )
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(
+ wmHelper,
+ stringExtras =
+ mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ )
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- testApp.exit(wmHelper)
- }
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
+ testApp.exit(wmHelper)
}
transitions {
// Enter PiP, and assert that the PiP is within bounds now that the device is back
// in portrait
broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- wmHelper.waitPipShown()
- wmHelper.waitForAppTransitionIdle()
// during rotation the status bar becomes invisible and reappears at the end
- wmHelper.waitForNavBarStatusBarVisible()
+ wmHelper
+ .StateSyncBuilder()
+ .withPipShown()
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
}
/**
- * Checks that the [FlickerComponentName.NAV_BAR] has the correct position at
- * the start and end of the transition
+ * This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to
+ * fix a orientation, Tablets instead keep the same orientation and add letterboxes
+ */
+ @Before
+ fun setup() {
+ Assume.assumeFalse(tapl.isTablet)
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] has the correct position at the start and end
+ * of the transition
*/
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ override fun navBarLayerPositionAtStartAndEnd() = flicker.navBarLayerPositionAtStartAndEnd()
/**
* Checks that all parts of the screen are covered at the start and end of the transition
@@ -119,103 +138,91 @@ class EnterPipToOtherOrientationTest(
*/
@Presubmit
@Test
- override fun entireScreenCovered() = testSpec.entireScreenCovered(allStates = false)
+ fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered(allStates = false)
- /**
- * Checks [pipApp] window remains visible and on top throughout the transition
- */
+ @FlakyTest(bugId = 251219769)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ /** Checks [pipApp] window remains visible and on top throughout the transition */
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
- testSpec.assertWm {
- isAppWindowOnTop(pipApp.component)
- }
+ flicker.assertWm { isAppWindowOnTop(pipApp) }
}
- /**
- * Checks that [testApp] window is not visible at the start
- */
+ /** Checks that [testApp] window is not visible at the start */
@Presubmit
@Test
fun testAppWindowInvisibleOnStart() {
- testSpec.assertWmStart {
- isAppWindowInvisible(testApp.component)
- }
+ flicker.assertWmStart { isAppWindowInvisible(testApp) }
}
- /**
- * Checks that [testApp] window is visible at the end
- */
+ /** Checks that [testApp] window is visible at the end */
@Presubmit
@Test
fun testAppWindowVisibleOnEnd() {
- testSpec.assertWmEnd {
- isAppWindowVisible(testApp.component)
- }
+ flicker.assertWmEnd { isAppWindowVisible(testApp) }
}
- /**
- * Checks that [testApp] layer is not visible at the start
- */
+ /** Checks that [testApp] layer is not visible at the start */
@Presubmit
@Test
fun testAppLayerInvisibleOnStart() {
- testSpec.assertLayersStart {
- isInvisible(testApp.component)
- }
+ flicker.assertLayersStart { isInvisible(testApp) }
}
- /**
- * Checks that [testApp] layer is visible at the end
- */
+ /** Checks that [testApp] layer is visible at the end */
@Presubmit
@Test
fun testAppLayerVisibleOnEnd() {
- testSpec.assertLayersEnd {
- isVisible(testApp.component)
- }
+ flicker.assertLayersEnd { isVisible(testApp) }
}
/**
- * Checks that the visible region of [pipApp] covers the full display area at the start of
- * the transition
+ * Checks that the visible region of [pipApp] covers the full display area at the start of the
+ * transition
*/
@Presubmit
@Test
fun pipAppLayerCoversFullScreenOnStart() {
- testSpec.assertLayersStart {
- visibleRegion(pipApp.component).coversExactly(startingBounds)
- }
+ flicker.assertLayersStart { visibleRegion(pipApp).coversExactly(startingBounds) }
}
/**
- * Checks that the visible region of [testApp] plus the visible region of [pipApp]
- * cover the full display area at the end of the transition
+ * Checks that the visible region of [testApp] plus the visible region of [pipApp] cover the
+ * full display area at the end of the transition
*/
@Presubmit
@Test
fun testAppPlusPipLayerCoversFullScreenOnEnd() {
- testSpec.assertLayersEnd {
- val pipRegion = visibleRegion(pipApp.component).region
- visibleRegion(testApp.component)
- .plus(pipRegion)
- .coversExactly(endingBounds)
+ flicker.assertLayersEnd {
+ val pipRegion = visibleRegion(pipApp).region
+ visibleRegion(testApp).plus(pipRegion).coversExactly(endingBounds)
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
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/ExitPipToAppTransition.kt
index 990872f58dc1..7466916a798a 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/ExitPipToAppTransition.kt
@@ -17,15 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Test
-/**
- * Base class for pip expand tests
- */
-abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- protected val testApp = FixedAppHelper(instrumentation)
+/** Base class for pip expand tests */
+abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flicker) {
+ protected val testApp = SimpleAppHelper(instrumentation)
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
@@ -34,9 +32,7 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun pipAppWindowRemainInsideVisibleBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
@@ -46,9 +42,7 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun pipAppLayerRemainInsideVisibleBounds() {
- testSpec.assertLayersVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
@@ -58,15 +52,15 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun showBothAppWindowsThenHidePip() {
- testSpec.assertWm {
+ flicker.assertWm {
// when the activity is STOPPING, sometimes it becomes invisible in an entry before
// the window, sometimes in the same entry. This occurs because we log 1x per frame
// thus we ignore activity here
- isAppWindowVisible(testApp.component)
- .isAppWindowOnTop(pipApp.component)
- .then()
- .isAppWindowInvisible(testApp.component)
- .isAppWindowVisible(pipApp.component)
+ isAppWindowVisible(testApp)
+ .isAppWindowOnTop(pipApp)
+ .then()
+ .isAppWindowInvisible(testApp)
+ .isAppWindowVisible(pipApp)
}
}
@@ -77,54 +71,46 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun showBothAppLayersThenHidePip() {
- testSpec.assertLayers {
- isVisible(testApp.component)
- .isVisible(pipApp.component)
- .then()
- .isInvisible(testApp.component)
- .isVisible(pipApp.component)
+ flicker.assertLayers {
+ isVisible(testApp).isVisible(pipApp).then().isInvisible(testApp).isVisible(pipApp)
}
}
/**
- * Checks that the visible region of [testApp] plus the visible region of [pipApp]
- * cover the full display area at the start of the transition
+ * Checks that the visible region of [testApp] plus the visible region of [pipApp] cover the
+ * full display area at the start of the transition
*/
@Presubmit
@Test
open fun testPlusPipAppsCoverFullScreenAtStart() {
- testSpec.assertLayersStart {
- val pipRegion = visibleRegion(pipApp.component).region
- visibleRegion(testApp.component)
- .plus(pipRegion)
- .coversExactly(displayBounds)
+ flicker.assertLayersStart {
+ val pipRegion = visibleRegion(pipApp).region
+ visibleRegion(testApp).plus(pipRegion).coversExactly(displayBounds)
}
}
/**
- * Checks that the visible region oft [pipApp] covers the full display area at the end of
- * the transition
+ * Checks that the visible region oft [pipApp] covers the full display area at the end of the
+ * transition
*/
@Presubmit
@Test
open fun pipAppCoversFullScreenAtEnd() {
- testSpec.assertLayersEnd {
- visibleRegion(pipApp.component).coversExactly(displayBounds)
- }
+ flicker.assertLayersEnd { visibleRegion(pipApp).coversExactly(displayBounds) }
}
- /**
- * Checks that the visible region of [pipApp] always expands during the animation
- */
+ /** Checks that the visible region of [pipApp] always expands during the animation */
@Presubmit
@Test
open fun pipLayerExpands() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
}
}
}
+
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 0b4bc761838d..1b5c227d37e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -17,36 +17,26 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.LAUNCHER
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Test
-/**
- * Base class for exiting pip (closing pip window) without returning to the app
- */
-abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+/** Base class for exiting pip (closing pip window) without returning to the app */
+abstract class ExitPipTransition(flicker: FlickerTest) : PipTransition(flicker) {
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
- setup {
- eachRun {
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- }
+ get() = buildTransition {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { this.setRotation(PlatformConsts.Rotation.ROTATION_0) }
}
/**
- * Checks that [pipApp] window is pinned and visible at the start and then becomes
- * unpinned and invisible at the same moment, and remains unpinned and invisible
- * until the end of the transition
+ * Checks that [pipApp] window is pinned and visible at the start and then becomes unpinned and
+ * invisible at the same moment, and remains unpinned and invisible until the end of the
+ * transition
*/
@Presubmit
@Test
@@ -55,42 +45,36 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition
// When Shell transition is enabled, we change the windowing mode at start, but
// update the visibility after the transition is finished, so we can't check isNotPinned
// and isAppWindowInvisible in the same assertion block.
- testSpec.assertWm {
+ flicker.assertWm {
this.invoke("hasPipWindow") {
- it.isPinned(pipApp.component)
- .isAppWindowVisible(pipApp.component)
- .isAppWindowOnTop(pipApp.component)
- }.then().invoke("!hasPipWindow") {
- it.isNotPinned(pipApp.component)
- .isAppWindowNotOnTop(pipApp.component)
- }
+ it.isPinned(pipApp).isAppWindowVisible(pipApp).isAppWindowOnTop(pipApp)
+ }
+ .then()
+ .invoke("!hasPipWindow") { it.isNotPinned(pipApp).isAppWindowNotOnTop(pipApp) }
}
- testSpec.assertWmEnd { isAppWindowInvisible(pipApp.component) }
+ flicker.assertWmEnd { isAppWindowInvisible(pipApp) }
} else {
- testSpec.assertWm {
- this.invoke("hasPipWindow") {
- it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
- }.then().invoke("!hasPipWindow") {
- it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
- }
+ flicker.assertWm {
+ this.invoke("hasPipWindow") { it.isPinned(pipApp).isAppWindowVisible(pipApp) }
+ .then()
+ .invoke("!hasPipWindow") { it.isNotPinned(pipApp).isAppWindowInvisible(pipApp) }
}
}
}
/**
- * Checks that [pipApp] and [LAUNCHER_COMPONENT] layers are visible at the start
- * of the transition. Then [pipApp] layer becomes invisible, and remains invisible
- * until the end of the transition
+ * Checks that [pipApp] and [LAUNCHER] layers are visible at the start of the transition. Then
+ * [pipApp] layer becomes invisible, and remains invisible until the end of the transition
*/
@Presubmit
@Test
open fun pipLayerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(pipApp.component)
- .isVisible(LAUNCHER_COMPONENT)
+ flicker.assertLayers {
+ this.isVisible(pipApp)
+ .isVisible(LAUNCHER)
.then()
- .isInvisible(pipApp.component)
- .isVisible(LAUNCHER_COMPONENT)
+ .isInvisible(pipApp)
+ .isVisible(LAUNCHER)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 0768e82e491c..1420f8ce653a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -16,14 +16,14 @@
package com.android.wm.shell.flicker.pip
-import android.view.Surface
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,65 +36,62 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExitPipViaExpandButtonClickTest`
*
* Actions:
+ * ```
* Launch an app in pip mode [pipApp],
* Launch another full screen mode [testApp]
* Expand [pipApp] app to full screen by clicking on the pip window and
* then on the expand button
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-@FlakyTest(bugId = 219750830)
-class ExitPipViaExpandButtonClickTest(
- testSpec: FlickerTestParameter
-) : ExitPipToAppTransition(testSpec) {
+class ExitPipViaExpandButtonClickTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
- /**
- * Defines the transition used to run the test
- */
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
setup {
- eachRun {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
}
transitions {
// This will bring PipApp to fullscreen
pipApp.expandPipWindowToApp(wmHelper)
// Wait until the other app is no longer visible
- wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
+ wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
}
}
- /** {@inheritDoc} */
- @FlakyTest(bugId = 197726610)
- @Test
- override fun pipLayerExpands() = super.pipLayerExpands()
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610) @Test override fun pipLayerExpands() = super.pipLayerExpands()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index c6a705dacb8d..dffbe7e8aef1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -16,16 +16,15 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -39,60 +38,61 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest`
*
* Actions:
+ * ```
* Launch an app in pip mode [pipApp],
* Launch another full screen mode [testApp]
* Expand [pipApp] app to full screen via an intent
- *
+ * ```
* 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
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
+class ExitPipViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
- /**
- * Defines the transition used to run the test
- */
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
setup {
- eachRun {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
}
transitions {
// This will bring PipApp to fullscreen
pipApp.exitPipToFullScreenViaIntent(wmHelper)
// Wait until the other app is no longer visible
- wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
+ wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
}
}
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
@Test
- override fun statusBarLayerRotatesScales() {
+ override fun statusBarLayerPositionAtStartAndEnd() {
Assume.assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
+ super.statusBarLayerPositionAtStartAndEnd()
}
@Presubmit
@Test
fun statusBarLayerRotatesScales_ShellTransit() {
Assume.assumeTrue(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
+ super.statusBarLayerPositionAtStartAndEnd()
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} */
@FlakyTest(bugId = 197726610)
@Test
override fun pipLayerExpands() {
@@ -111,14 +111,15 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
+ * and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 437ad893f1d9..232c025e60a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -17,14 +17,12 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,63 +35,56 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest`
*
* Actions:
+ * ```
* Launch an app in pip mode [pipApp],
* Click on the pip window
* Click on dismiss button and wait window disappear
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
+class ExitPipWithDismissButtonTest(flicker: FlickerTest) : ExitPipTransition(flicker) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
- transitions {
- pipApp.closePipWindow(wmHelper)
- }
+ transitions { pipApp.closePipWindow(wmHelper) }
}
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
/**
* Checks that the focus changes between the pip menu window and the launcher when clicking the
* dismiss button on pip menu to close the pip window.
*/
@Presubmit
@Test
- fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusChanges("PipMenuView", "NexusLauncherActivity")
- }
+ fun focusChanges() {
+ flicker.assertEventLog { this.focusChanges("PipMenuView", "NexusLauncherActivity") }
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
+ * and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 128703ad332c..dbbfdcc8803a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -17,14 +17,13 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,70 +36,78 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest`
*
* Actions:
+ * ```
* Launch an app in pip mode [pipApp],
* Swipe the pip window to the bottom-center of the screen and wait it disappear
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
+class ExitPipWithSwipeDownTest(flicker: FlickerTest) : ExitPipTransition(flicker) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
- val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
+ val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
val pipCenterX = pipRegion.centerX()
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
- device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
- wmHelper.waitPipGone()
- wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
- wmHelper.waitForAppTransitionIdle()
+ val barComponent =
+ if (flicker.scenario.isTablet) {
+ ComponentNameMatcher.TASK_BAR
+ } else {
+ ComponentNameMatcher.NAV_BAR
+ }
+ val barLayerHeight =
+ wmHelper.currentState.layerState
+ .getLayerWithBuffer(barComponent)
+ ?.visibleRegion
+ ?.height
+ ?: error("Couldn't find Nav or Task bar layer")
+ // The dismiss button doesn't appear at the complete bottom of the screen,
+ val displayY = device.displayHeight - barLayerHeight
+ device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50)
+ // Wait until the other app is no longer visible
+ wmHelper
+ .StateSyncBuilder()
+ .withPipGone()
+ .withWindowSurfaceDisappeared(pipApp)
+ .withAppTransitionIdle()
+ .waitForAndVerify()
}
}
- @FlakyTest
- @Test
- override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
-
- @FlakyTest
- @Test
- override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
-
- /**
- * Checks that the focus doesn't change between windows during the transition
- */
+ /** Checks that the focus doesn't change between windows during the transition */
@Presubmit
@Test
fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
+ flicker.assertEventLog { this.focusDoesNotChange() }
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
+ * and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.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 28b7fc9bd29e..f213cc96ecdb 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
@@ -16,16 +16,14 @@
package com.android.wm.shell.flicker.pip
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
+import android.platform.test.annotations.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,153 +36,185 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:ExpandPipOnDoubleClickTest`
*
* Actions:
+ * ```
* Launch an app in pip mode [pipApp],
* Expand [pipApp] app to its maximum pip size by double clicking on it
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) {
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
- transitions {
- pipApp.doubleClickPipWindow(wmHelper)
- }
- }
+ get() = buildTransition { transitions { pipApp.doubleClickPipWindow(wmHelper) } }
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
- @Presubmit
+ @FlakyTest(bugId = 249308003)
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
- @Presubmit
+ @FlakyTest(bugId = 249308003)
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayersVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
- /**
- * Checks [pipApp] window remains visible throughout the animation
- */
- @Presubmit
+ /** Checks [pipApp] window remains visible throughout the animation */
+ @FlakyTest(bugId = 249308003)
@Test
fun pipWindowIsAlwaysVisible() {
- testSpec.assertWm {
- isAppWindowVisible(pipApp.component)
- }
+ flicker.assertWm { isAppWindowVisible(pipApp) }
}
- /**
- * Checks [pipApp] layer remains visible throughout the animation
- */
- @Presubmit
+ /** Checks [pipApp] layer remains visible throughout the animation */
+ @FlakyTest(bugId = 249308003)
@Test
fun pipLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- isVisible(pipApp.component)
- }
+ flicker.assertLayers { isVisible(pipApp) }
}
- /**
- * Checks that the visible region of [pipApp] always expands during the animation
- */
- @FlakyTest(bugId = 228012337)
+ /** Checks that the visible region of [pipApp] always expands during the animation */
+ @FlakyTest(bugId = 249308003)
@Test
fun pipLayerExpands() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
}
}
}
- @Presubmit
+ @FlakyTest(bugId = 249308003)
@Test
fun pipSameAspectRatio() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.isSameAspectRatio(previous.visibleRegion)
}
}
}
- /**
- * Checks [pipApp] window remains pinned throughout the animation
- */
- @Presubmit
+ /** Checks [pipApp] window remains pinned throughout the animation */
+ @FlakyTest(bugId = 249308003)
@Test
fun windowIsAlwaysPinned() {
- testSpec.assertWm {
- this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
- }
+ flicker.assertWm { this.invoke("hasPipWindow") { it.isPinned(pipApp) } }
}
- /**
- * Checks [pipApp] layer remains visible throughout the animation
- */
- @Presubmit
+ /** Checks [ComponentMatcher.LAUNCHER] layer remains visible throughout the animation */
+ @FlakyTest(bugId = 249308003)
@Test
fun launcherIsAlwaysVisible() {
- testSpec.assertLayers {
- isVisible(LAUNCHER_COMPONENT)
- }
+ flicker.assertLayers { isVisible(ComponentNameMatcher.LAUNCHER) }
}
- /**
- * Checks that the focus doesn't change between windows during the transition
- */
+ /** Checks that the focus doesn't change between windows during the transition */
@FlakyTest(bugId = 216306753)
@Test
fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
+ flicker.assertEventLog { this.focusDoesNotChange() }
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest(bugId = 216306753)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
}
- @FlakyTest(bugId = 206753786)
+ @FlakyTest(bugId = 216306753)
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.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
new file mode 100644
index 000000000000..34f6659ca36e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+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)
+class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.4f, 30) } }
+
+ /** Checks that the visible region area of [pipApp] always increases during the animation. */
+ @Postsubmit
+ @Test
+ fun pipLayerAreaIncreases() {
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ previous.visibleRegion.notBiggerThan(current.visibleRegion.region)
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index 8729bb6776f0..16acc11f5729 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -16,15 +16,14 @@
package com.android.wm.shell.flicker.pip
-import android.view.Surface
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.region.RegionSubject
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,71 +31,62 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip movement with Launcher shelf height change (decrease).
+ * Test Pip movement with Launcher shelf height change (increase).
*
- * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
*
* Actions:
+ * ```
* Launch [pipApp] in pip mode
- * Launch [testApp]
* Press home
+ * Launch [testApp]
* Check if pip window moves down (visually)
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-open class MovePipDownShelfHeightChangeTest(
- testSpec: FlickerTestParameter
-) : MovePipShelfHeightTransition(testSpec) {
- /**
- * Defines the transition used to run the test
- */
+class MovePipDownShelfHeightChangeTest(flicker: FlickerTest) :
+ MovePipShelfHeightTransition(flicker) {
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
teardown {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
- test {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- taplInstrumentation.pressHome()
+ tapl.pressHome()
+ testApp.exit(wmHelper)
}
+ transitions { testApp.launchViaIntent(wmHelper) }
}
- override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
- current.isHigherOrEqual(previous.region)
- }
+ /** Checks that the visible region of [pipApp] window always moves down during the animation. */
+ @Presubmit @Test fun pipWindowMovesDown() = pipWindowMoves(Direction.DOWN)
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** Checks that the visible region of [pipApp] layer always moves down during the animation. */
+ @Presubmit @Test fun pipLayerMovesDown() = pipLayerMoves(Direction.DOWN)
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
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/MovePipShelfHeightTransition.kt
index 0499e7de9a0a..35525cbd4e1a 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/MovePipShelfHeightTransition.kt
@@ -17,46 +17,28 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.wm.shell.flicker.Direction
import org.junit.Test
-/**
- * Base class for pip tests with Launcher shelf height change
- */
-abstract class MovePipShelfHeightTransition(
- testSpec: FlickerTestParameter
-) : PipTransition(testSpec) {
- protected val taplInstrumentation = LauncherInstrumentation()
- protected val testApp = FixedAppHelper(instrumentation)
-
- /**
- * Checks if the window movement direction is valid
- */
- protected abstract fun assertRegionMovement(previous: RegionSubject, current: RegionSubject)
+/** Base class for pip tests with Launcher shelf height change */
+abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransition(flicker) {
+ protected val testApp = FixedOrientationAppHelper(instrumentation)
- /**
- * Checks [pipApp] window remains visible throughout the animation
- */
+ /** Checks [pipApp] window remains visible throughout the animation */
@Presubmit
@Test
open fun pipWindowIsAlwaysVisible() {
- testSpec.assertWm {
- isAppWindowVisible(pipApp.component)
- }
+ flicker.assertWm { isAppWindowVisible(pipApp) }
}
- /**
- * Checks [pipApp] layer remains visible throughout the animation
- */
+ /** Checks [pipApp] layer remains visible throughout the animation */
@Presubmit
@Test
open fun pipLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- isVisible(pipApp.component)
- }
+ flicker.assertLayers { isVisible(pipApp) }
}
/**
@@ -66,9 +48,7 @@ abstract class MovePipShelfHeightTransition(
@Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
@@ -78,39 +58,49 @@ abstract class MovePipShelfHeightTransition(
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayersVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
+ flicker.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
/**
- * Checks that the visible region of [pipApp] always moves in the correct direction
+ * Checks that the visible region of [pipApp] window always moves in the specified direction
* during the animation.
*/
- @Presubmit
- @Test
- open fun pipWindowMoves() {
- val windowName = pipApp.component.toWindowName()
- testSpec.assertWm {
- val pipWindowList = this.windowStates { it.name.contains(windowName) && it.isVisible }
- pipWindowList.zipWithNext { previous, current ->
- assertRegionMovement(previous.frame, current.frame)
+ protected fun pipWindowMoves(direction: Direction) {
+ flicker.assertWm {
+ val pipWindowFrameList =
+ this.windowStates { pipApp.windowMatchesAnyOf(it) && it.isVisible }.map { it.frame }
+ when (direction) {
+ Direction.UP -> assertRegionMovementUp(pipWindowFrameList)
+ Direction.DOWN -> assertRegionMovementDown(pipWindowFrameList)
+ else -> error("Unhandled direction")
}
}
}
/**
- * Checks that the visible region of [pipApp] always moves up during the animation
+ * Checks that the visible region of [pipApp] layer always moves in the specified direction
+ * during the animation.
*/
- @Presubmit
- @Test
- open fun pipLayerMoves() {
- val layerName = pipApp.component.toLayerName()
- testSpec.assertLayers {
- val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
- pipLayerList.zipWithNext { previous, current ->
- assertRegionMovement(previous.visibleRegion, current.visibleRegion)
+ protected fun pipLayerMoves(direction: Direction) {
+ flicker.assertLayers {
+ val pipLayerRegionList =
+ this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ .map { it.visibleRegion }
+ when (direction) {
+ Direction.UP -> assertRegionMovementUp(pipLayerRegionList)
+ Direction.DOWN -> assertRegionMovementDown(pipLayerRegionList)
+ else -> error("Unhandled direction")
}
}
}
-} \ No newline at end of file
+
+ private fun assertRegionMovementDown(regions: List<RegionSubject>) {
+ regions.zipWithNext { previous, current -> current.isLowerOrEqual(previous) }
+ regions.last().isLower(regions.first())
+ }
+
+ private fun assertRegionMovementUp(regions: List<RegionSubject>) {
+ regions.zipWithNext { previous, current -> current.isHigherOrEqual(previous.region) }
+ regions.last().isHigher(regions.first())
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 388b5e0b5e47..3a12a34a5206 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,18 +16,14 @@
package com.android.wm.shell.flicker.pip
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import org.junit.Assume
-import org.junit.Before
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,76 +31,61 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip movement with Launcher shelf height change (increase).
+ * Test Pip movement with Launcher shelf height change (decrease).
*
- * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
*
* Actions:
+ * ```
* Launch [pipApp] in pip mode
- * Press home
* Launch [testApp]
+ * Press home
* Check if pip window moves up (visually)
- *
+ * ```
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group3
-class MovePipUpShelfHeightChangeTest(
- testSpec: FlickerTestParameter
-) : MovePipShelfHeightTransition(testSpec) {
- @Before
- fun before() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- }
-
- /**
- * Defines the transition used to run the test
- */
+open class MovePipUpShelfHeightChangeTest(flicker: FlickerTest) :
+ MovePipShelfHeightTransition(flicker) {
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
- teardown {
- eachRun {
- taplInstrumentation.pressHome()
- }
- test {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
+ get() =
+ buildTransition() {
+ setup { testApp.launchViaIntent(wmHelper) }
+ transitions { tapl.pressHome() }
+ teardown { testApp.exit(wmHelper) }
}
- }
- override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
- current.isLowerOrEqual(previous.region)
- }
+ /** Checks that the visible region of [pipApp] window always moves up during the animation. */
+ @Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** Checks that the visible region of [pipApp] layer always moves up during the animation. */
+ @Presubmit @Test fun pipLayerMovesUp() = pipLayerMoves(Direction.UP)
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 1e30f6b83874..12d6362cb060 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -17,19 +17,17 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.service.PlatformConsts
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
@@ -38,17 +36,12 @@ import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/**
- * Test Pip launch.
- * To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
- */
+/** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-@FlakyTest(bugId = 218604389)
-open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+open class PipKeyboardTest(flicker: FlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
@Before
@@ -56,19 +49,16 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
assumeFalse(isShellTransitionsEnabled)
}
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
setup {
- test {
- imeApp.launchViaIntent(wmHelper)
- setRotation(testSpec.startRotation)
- }
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(flicker.scenario.startRotation)
}
teardown {
- test {
- imeApp.exit(wmHelper)
- setRotation(Surface.ROTATION_0)
- }
+ imeApp.exit(wmHelper)
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
}
transitions {
// open the soft keyboard
@@ -80,32 +70,21 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
}
}
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- /**
- * Ensure the pip window remains visible throughout any keyboard interactions
- */
+ /** Ensure the pip window remains visible throughout any keyboard interactions */
@Presubmit
@Test
open fun pipInVisibleBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ flicker.assertWmVisibleRegion(pipApp) {
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
coversAtMost(displayBounds)
}
}
- /**
- * Ensure that the pip window does not obscure the keyboard
- */
+ /** Ensure that the pip window does not obscure the keyboard */
@Presubmit
@Test
open fun pipIsAboveAppWindow() {
- testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(FlickerComponentName.IME, pipApp.component)
- }
+ flicker.assertWmTag(TAG_IME_VISIBLE) { isAboveWindow(ComponentNameMatcher.IME, pipApp) }
}
companion object {
@@ -113,10 +92,10 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
index fe51228230cb..901814e21971 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
@@ -18,10 +18,9 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -34,8 +33,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardTest(testSpec) {
+class PipKeyboardTestShellTransit(flicker: FlickerTest) : PipKeyboardTest(flicker) {
@Before
override fun before() {
@@ -44,5 +42,5 @@ class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardT
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-} \ No newline at end of file
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 9fad4997e63a..eee00bd1e699 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -16,20 +16,19 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import org.junit.Assume
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -42,146 +41,140 @@ import org.junit.runners.Parameterized
* To run this test: `atest WMShellFlickerTests:PipRotationTest`
*
* Actions:
+ * ```
* Launch a [pipApp] in pip mode
* Launch another app [fixedApp] (appears below pip)
- * Rotate the screen from [testSpec.startRotation] to [testSpec.endRotation]
+ * Rotate the screen from [flicker.scenario.startRotation] to [flicker.scenario.endRotation]
* (usually, 0->90 and 90->0)
- *
+ * ```
* 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
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* 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)
-@Group4
-open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- private val fixedApp = FixedAppHelper(instrumentation)
- private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
- private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.endRotation)
+open class PipRotationTest(flicker: FlickerTest) : PipTransition(flicker) {
+ private val testApp = SimpleAppHelper(instrumentation)
+ private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
+ private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
+
+ @Before
+ open fun before() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ }
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
setup {
- test {
- fixedApp.launchViaIntent(wmHelper)
- }
- eachRun {
- setRotation(testSpec.startRotation)
- }
- }
- transitions {
- setRotation(testSpec.endRotation)
+ testApp.launchViaIntent(wmHelper)
+ setRotation(flicker.scenario.startRotation)
}
+ transitions { setRotation(flicker.scenario.endRotation) }
}
- /**
- * Checks that all parts of the screen are covered at the start and end of the transition
- */
+ /** Checks the position of the navigation bar at the start and end of the transition */
+ @FlakyTest(bugId = 240499181)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition */
@Presubmit
@Test
- override fun entireScreenCovered() = testSpec.entireScreenCovered()
+ fun fixedAppLayer_StartingBounds() {
+ flicker.assertLayersStart { visibleRegion(testApp).coversAtMost(screenBoundsStart) }
+ }
- /**
- * Checks the position of the navigation bar at the start and end of the transition
- */
- @FlakyTest
+ /** Checks that [testApp] layer is within [screenBoundsEnd] at the end of the transition */
+ @Presubmit
@Test
- override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ fun fixedAppLayer_EndingBounds() {
+ flicker.assertLayersEnd { visibleRegion(testApp).coversAtMost(screenBoundsEnd) }
+ }
/**
- * Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
+ * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the start of the
+ * transition
*/
@Presubmit
@Test
- fun appLayerRotates_StartingBounds() {
- testSpec.assertLayersStart {
- visibleRegion(fixedApp.component).coversExactly(screenBoundsStart)
+ fun appLayers_StartingBounds() {
+ flicker.assertLayersStart {
+ visibleRegion(testApp.or(pipApp)).coversExactly(screenBoundsStart)
}
}
/**
- * Checks that [fixedApp] layer is within [screenBoundsEnd] at the end of the transition
+ * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the end of the
+ * transition
*/
@Presubmit
@Test
- fun appLayerRotates_EndingBounds() {
- testSpec.assertLayersEnd {
- visibleRegion(fixedApp.component).coversExactly(screenBoundsEnd)
- }
+ fun appLayers_EndingBounds() {
+ flicker.assertLayersEnd { visibleRegion(testApp.or(pipApp)).coversExactly(screenBoundsEnd) }
}
- /**
- * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition
- */
+ /** Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition */
private fun pipLayerRotates_StartingBounds_internal() {
- testSpec.assertLayersStart {
- visibleRegion(pipApp.component).coversAtMost(screenBoundsStart)
- }
+ flicker.assertLayersStart { visibleRegion(pipApp).coversAtMost(screenBoundsStart) }
}
- /**
- * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition
- */
+ /** Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition */
@Presubmit
@Test
fun pipLayerRotates_StartingBounds() {
pipLayerRotates_StartingBounds_internal()
}
- /**
- * Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition
- */
+ /** Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition */
@Presubmit
@Test
fun pipLayerRotates_EndingBounds() {
- testSpec.assertLayersEnd {
- visibleRegion(pipApp.component).coversAtMost(screenBoundsEnd)
- }
+ flicker.assertLayersEnd { visibleRegion(pipApp).coversAtMost(screenBoundsEnd) }
}
/**
- * Ensure that the [pipApp] window does not obscure the [fixedApp] at the start of the
- * transition
+ * Ensure that the [pipApp] window does not obscure the [testApp] at the start of the transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_Start() {
- testSpec.assertWmStart {
- isAboveWindow(pipApp.component, fixedApp.component)
- }
+ flicker.assertWmStart { isAboveWindow(pipApp, testApp) }
}
/**
- * Ensure that the [pipApp] window does not obscure the [fixedApp] at the end of the
- * transition
+ * Ensure that the [pipApp] window does not obscure the [testApp] at the end of the transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_End() {
- testSpec.assertWmEnd {
- isAboveWindow(pipApp.component, fixedApp.component)
- }
+ flicker.assertWmEnd { isAboveWindow(pipApp, testApp) }
+ }
+
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
+ * and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
- repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt
new file mode 100644
index 000000000000..d0d9167555eb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip Stack in bounds after rotations.
+ *
+ * To run this test: `atest WMShellFlickerTests:PipRotationTest_ShellTransit`
+ *
+ * Actions:
+ * ```
+ * Launch a [pipApp] in pip mode
+ * Launch another app [fixedApp] (appears below pip)
+ * Rotate the screen from [flicker.scenario.startRotation] to [flicker.scenario.endRotation]
+ * (usually, 0->90 and 90->0)
+ * ```
+ * 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
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * 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)
+@FlakyTest(bugId = 239575053)
+class PipRotationTest_ShellTransit(flicker: FlickerTest) : PipRotationTest(flicker) {
+ @Before
+ override fun before() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
deleted file mode 100644
index 7ba085d3cf1a..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import com.android.wm.shell.flicker.FlickerTestBase
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import org.junit.Before
-
-abstract class PipTestBase(
- rotationName: String,
- rotation: Int
-) : FlickerTestBase(rotationName, rotation) {
- protected val testApp = PipAppHelper(instrumentation)
-
- @Before
- override fun televisionSetUp() {
- /**
- * The super implementation assumes ([org.junit.Assume]) that not running on TV, thus
- * disabling the test on TV. This test, however, *should run on TV*, so we overriding this
- * method and simply leaving it blank.
- */
- }
-}
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/PipTransition.kt
index 8d542c8ec9e6..0e0be79e0aa0 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/PipTransition.kt
@@ -18,33 +18,21 @@ package com.android.wm.shell.flicker.pip
import android.app.Instrumentation
import android.content.Intent
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Test
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.BaseTest
-abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
protected val pipApp = PipAppHelper(instrumentation)
- protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
- protected abstract val transition: FlickerBuilder.() -> Unit
+
// Helper class to process test actions by broadcast.
protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -52,123 +40,44 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
}
fun doAction(broadcastAction: String) {
- instrumentation.context
- .sendBroadcast(createIntentWithAction(broadcastAction))
+ instrumentation.context.sendBroadcast(createIntentWithAction(broadcastAction))
}
companion object {
// Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
- @JvmStatic
- val ORIENTATION_LANDSCAPE = 0
+ @JvmStatic val ORIENTATION_LANDSCAPE = 0
// Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- @JvmStatic
- val ORIENTATION_PORTRAIT = 1
+ @JvmStatic val ORIENTATION_PORTRAIT = 1
}
}
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition(this)
- }
- }
-
- /**
- * Gets a configuration that handles basic setup and teardown of pip tests
- */
- protected val setupAndTeardown: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- }
- }
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- test {
- removeAllTasksButHome()
- pipApp.exit(wmHelper)
- }
- }
- }
-
/**
- * Gets a configuration that handles basic setup and teardown of pip tests and that
- * launches the Pip app for test
+ * Gets a configuration that handles basic setup and teardown of pip tests and that launches the
+ * Pip app for test
*
* @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
* @param stringExtras Arguments to pass to the PIP launch intent
- * @param extraSpec Addicional segment of flicker specification
+ * @param extraSpec Additional segment of flicker specification
*/
@JvmOverloads
protected open fun buildTransition(
- eachRun: Boolean,
- stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
+ stringExtras: Map<String, String> = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"),
extraSpec: FlickerBuilder.() -> Unit = {}
): FlickerBuilder.() -> Unit {
return {
- setupAndTeardown(this)
-
setup {
- test {
- if (!eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- wmHelper.waitPipShown()
- }
- }
- eachRun {
- if (eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- wmHelper.waitPipShown()
- }
- }
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ removeAllTasksButHome()
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
}
teardown {
- eachRun {
- if (eachRun) {
- pipApp.exit(wmHelper)
- }
- }
- test {
- if (!eachRun) {
- pipApp.exit(wmHelper)
- }
- }
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
extraSpec(this)
}
}
-
- @Presubmit
- @Test
- open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @Presubmit
- @Test
- open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- open fun entireScreenCovered() = testSpec.entireScreenCovered()
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 51339a1deb4b..7d5dd8939899 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,22 +16,26 @@
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.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
+import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
-import com.android.wm.shell.flicker.testapp.Components
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.Assume
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,127 +43,153 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test exiting Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
+ * Test exiting Pip with orientation changes. To run this test: `atest
+ * WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-open class SetRequestedOrientationWhilePinnedTest(
- testSpec: FlickerTestParameter
-) : PipTransition(testSpec) {
- private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+open class SetRequestedOrientationWhilePinnedTest(flicker: FlickerTest) : PipTransition(flicker) {
+ private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0)
+ private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90)
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- // Launch the PiP activity fixed as landscape.
- pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- // Enter PiP.
- broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP)
- wmHelper.waitPipShown()
- wmHelper.waitForRotation(Surface.ROTATION_0)
- wmHelper.waitForAppTransitionIdle()
- // System bar may fade out during fixed rotation.
- wmHelper.waitForNavBarStatusBarVisible()
- }
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+
+ // Launch the PiP activity fixed as landscape.
+ pipApp.launchViaIntent(
+ wmHelper,
+ stringExtras =
+ mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ )
+ // Enter PiP.
+ broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
+ // System bar may fade out during fixed rotation.
+ wmHelper
+ .StateSyncBuilder()
+ .withPipShown()
+ .withRotation(PlatformConsts.Rotation.ROTATION_0)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- setRotation(Surface.ROTATION_0)
- }
- test {
- removeAllTasksButHome()
- }
+ pipApp.exit(wmHelper)
+ setRotation(PlatformConsts.Rotation.ROTATION_0)
+ removeAllTasksButHome()
}
transitions {
// Launch the activity back into fullscreen and ensure that it is now in landscape
pipApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(pipApp.component)
- wmHelper.waitForRotation(Surface.ROTATION_90)
- wmHelper.waitForAppTransitionIdle()
// System bar may fade out during fixed rotation.
- wmHelper.waitForNavBarStatusBarVisible()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(pipApp)
+ .withRotation(PlatformConsts.Rotation.ROTATION_90)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
}
+ /**
+ * This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to
+ * fix a orientation, Tablets instead keep the same orientation and add letterboxes
+ */
+ @Before
+ fun setup() {
+ Assume.assumeFalse(tapl.isTablet)
+ }
+
@Presubmit
@Test
fun displayEndsAt90Degrees() {
- testSpec.assertWmEnd {
- hasRotation(Surface.ROTATION_90)
- }
+ flicker.assertWmEnd { hasRotation(PlatformConsts.Rotation.ROTATION_90) }
}
+ /** {@inheritDoc} */
@Presubmit
@Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+ /** {@inheritDoc} */
@Presubmit
@Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ /** {@inheritDoc} */
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
@Presubmit
@Test
fun pipWindowInsideDisplay() {
- testSpec.assertWmStart {
- frameRegion(pipApp.component).coversAtMost(startingBounds)
- }
+ flicker.assertWmStart { visibleRegion(pipApp).coversAtMost(startingBounds) }
}
@Presubmit
@Test
fun pipAppShowsOnTop() {
- testSpec.assertWmEnd {
- isAppWindowOnTop(pipApp.component)
- }
+ flicker.assertWmEnd { isAppWindowOnTop(pipApp) }
+ }
+
+ private fun pipLayerInsideDisplay_internal() {
+ flicker.assertLayersStart { visibleRegion(pipApp).coversAtMost(startingBounds) }
}
@Presubmit
@Test
fun pipLayerInsideDisplay() {
- testSpec.assertLayersStart {
- visibleRegion(pipApp.component).coversAtMost(startingBounds)
- }
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ pipLayerInsideDisplay_internal()
+ }
+
+ @FlakyTest(bugId = 250527829)
+ @Test
+ fun pipLayerInsideDisplay_shellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ pipLayerInsideDisplay_internal()
}
@Presubmit
@Test
fun pipAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(pipApp.component)
- }
+ flicker.assertWm { this.isAppWindowVisible(pipApp) }
}
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
- testSpec.assertLayersEnd {
- visibleRegion(pipApp.component).coversExactly(endingBounds)
- }
+ flicker.assertLayersEnd { visibleRegion(pipApp).coversExactly(endingBounds) }
}
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 1)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0)
+ )
}
}
}
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
new file mode 100644
index 000000000000..36909dd74245
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.flicker.pip.tv
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+/** Helper class for PIP app on AndroidTV */
+open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) {
+ private val appSelector = By.pkg(`package`).depth(0)
+
+ val ui: UiObject2?
+ get() = uiDevice.findObject(appSelector)
+
+ private fun focusOnObject(selector: BySelector): Boolean {
+ // We expect all the focusable UI elements to be arranged in a way so that it is possible
+ // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
+ // from "the bottom".
+ repeat(FOCUS_ATTEMPTS) {
+ uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true }
+ ?: error("The object we try to focus on is gone.")
+
+ uiDevice.pressDPadDown()
+ uiDevice.waitForIdle()
+ }
+ return false
+ }
+
+ override fun clickObject(resId: String) {
+ val selector = By.res(`package`, resId)
+ focusOnObject(selector) || error("Could not focus on `$resId` object")
+ uiDevice.pressDPadCenter()
+ }
+
+ @Deprecated(
+ "Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)")
+ )
+ override fun closePipWindow() {
+ uiDevice.closeTvPipWindow()
+ }
+
+ /** Taps the pip window and dismisses it by clicking on the X button. */
+ override fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ uiDevice.closeTvPipWindow()
+
+ // Wait for animation to complete.
+ wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ fun waitUntilClosed(): Boolean {
+ val appSelector = By.pkg(`package`).depth(0)
+ return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
+ }
+
+ companion object {
+ private const val FOCUS_ATTEMPTS = 20
+ private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
index 9c50630095be..2cb18f948f0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,47 +14,36 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker
+package com.android.wm.shell.flicker.pip.tv
import android.app.Instrumentation
import android.content.pm.PackageManager
-import android.content.pm.PackageManager.FEATURE_LEANBACK
-import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
-import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.runners.Parameterized
-/**
- * Base class of all Flicker test that performs common functions for all flicker tests:
- *
- * - Caches transitions so that a transition is run once and the transition results are used by
- * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
- * multiple times.
- * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
- * - Fails tests if results are not available for any test due to jank.
- */
-abstract class FlickerTestBase(
- protected val rotationName: String,
- protected val rotation: Int
-) {
+abstract class PipTestBase(protected val rotationName: String, protected val rotation: Int) {
val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
val uiDevice = UiDevice.getInstance(instrumentation)
val packageManager: PackageManager = instrumentation.context.packageManager
protected val isTelevision: Boolean by lazy {
packageManager.run {
- hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
+ hasSystemFeature(PackageManager.FEATURE_LEANBACK) ||
+ hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)
}
}
+ protected val testApp = PipAppHelperTv(instrumentation)
- /**
- * By default WmShellFlickerTests do not run on TV devices.
- * If the test should run on TV - it should override this method.
- */
@Before
- open fun televisionSetUp() = assumeFalse(isTelevision)
+ open fun televisionSetUp() {
+ /**
+ * The super implementation assumes ([org.junit.Assume]) that not running on TV, thus
+ * disabling the test on TV. This test, however, *should run on TV*, so we overriding this
+ * method and simply leaving it blank.
+ */
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
index 49094e609fbc..8a073abf032c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
@@ -25,16 +25,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-/**
- * Test Pip Menu on TV.
- * To run this test: `atest WMShellFlickerTests:TvPipBasicTest`
- */
+/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipBasicTest` */
@RequiresDevice
@RunWith(Parameterized::class)
-class TvPipBasicTest(
- private val radioButtonId: String,
- private val pipWindowRatio: Rational?
-) : TvPipTestBase() {
+class TvPipBasicTest(private val radioButtonId: String, private val pipWindowRatio: Rational?) :
+ TvPipTestBase() {
@Test
fun enterPip_openMenu_pressBack_closePip() {
@@ -43,10 +38,10 @@ class TvPipBasicTest(
// Set up ratio and enter Pip
testApp.clickObject(radioButtonId)
- testApp.clickEnterPipButton()
+ testApp.clickEnterPipButton(wmHelper)
- val actualRatio: Float = testApp.ui?.visibleBounds?.ratio
- ?: fail("Application UI not found")
+ val actualRatio: Float =
+ testApp.ui?.visibleBounds?.ratio ?: fail("Application UI not found")
pipWindowRatio?.let { expectedRatio ->
assertEquals("Wrong Pip window ratio", expectedRatio.toFloat(), actualRatio)
}
@@ -62,7 +57,8 @@ class TvPipBasicTest(
// Make sure Pip Window ration remained the same after Pip menu was closed
testApp.ui?.visibleBounds?.let { newBounds ->
assertEquals("Pip window ratio has changed", actualRatio, newBounds.ratio)
- } ?: fail("Application UI not found")
+ }
+ ?: fail("Application UI not found")
// Close Pip
testApp.closePipWindow()
@@ -77,11 +73,11 @@ class TvPipBasicTest(
fun getParams(): Collection<Array<Any?>> {
infix fun Int.to(denominator: Int) = Rational(this, denominator)
return listOf(
- arrayOf("ratio_default", null),
- arrayOf("ratio_square", 1 to 1),
- arrayOf("ratio_wide", 2 to 1),
- arrayOf("ratio_tall", 1 to 2)
+ arrayOf("ratio_default", null),
+ arrayOf("ratio_square", 1 to 1),
+ arrayOf("ratio_wide", 2 to 1),
+ arrayOf("ratio_tall", 1 to 2)
)
}
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 061218a015e4..7403aab7d4c0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -19,36 +19,34 @@ package com.android.wm.shell.flicker.pip.tv
import android.graphics.Rect
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.UiObject2
+import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.testapp.Components
import com.android.wm.shell.flicker.wait
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
-/**
- * Test Pip Menu on TV.
- * To run this test: `atest WMShellFlickerTests:TvPipMenuTests`
- */
+/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipMenuTests` */
@RequiresDevice
class TvPipMenuTests : TvPipTestBase() {
private val systemUiResources =
- packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
- private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
- val bounds = getString(getIdentifier("pip_menu_bounds", "string",
- SYSTEM_UI_PACKAGE_NAME))
- Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
- }
- private val playButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_play", "string",
- SYSTEM_UI_PACKAGE_NAME))
- }
- private val pauseButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_pause", "string",
- SYSTEM_UI_PACKAGE_NAME))
- }
+ packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
+ private val pipBoundsWhileInMenu: Rect =
+ systemUiResources.run {
+ val bounds =
+ getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+ Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
+ }
+ private val playButtonDescription =
+ systemUiResources.run {
+ getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
+ }
+ private val pauseButtonDescription =
+ systemUiResources.run {
+ getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME))
+ }
@Before
fun tvPipMenuTestsTestUp() {
@@ -61,20 +59,29 @@ class TvPipMenuTests : TvPipTestBase() {
enterPip_openMenu_assertShown()
// Make sure the PiP task is positioned where it should be.
- val activityBounds: Rect = testApp.ui?.visibleBounds
- ?: error("Could not retrieve Pip Activity bounds")
- assertTrue("Pip Activity is positioned correctly while Pip menu is shown",
- pipBoundsWhileInMenu == activityBounds)
+ val activityBounds: Rect =
+ testApp.ui?.visibleBounds ?: error("Could not retrieve Pip Activity bounds")
+ assertTrue(
+ "Pip Activity is positioned correctly while Pip menu is shown",
+ pipBoundsWhileInMenu == activityBounds
+ )
// Make sure the Pip Menu Actions are positioned correctly.
uiDevice.findTvPipMenuControls()?.visibleBounds?.run {
- assertTrue("Pip Menu Actions should be positioned below the Activity in Pip",
- top >= activityBounds.bottom)
- assertTrue("Pip Menu Actions should be positioned central horizontally",
- centerX() == uiDevice.displayWidth / 2)
- assertTrue("Pip Menu Actions should be fully shown on the screen",
- left >= 0 && right <= uiDevice.displayWidth && bottom <= uiDevice.displayHeight)
- } ?: error("Could not retrieve Pip Menu Actions bounds")
+ assertTrue(
+ "Pip Menu Actions should be positioned below the Activity in Pip",
+ top >= activityBounds.bottom
+ )
+ assertTrue(
+ "Pip Menu Actions should be positioned central horizontally",
+ centerX() == uiDevice.displayWidth / 2
+ )
+ assertTrue(
+ "Pip Menu Actions should be fully shown on the screen",
+ left >= 0 && right <= uiDevice.displayWidth && bottom <= uiDevice.displayHeight
+ )
+ }
+ ?: error("Could not retrieve Pip Menu Actions bounds")
testApp.closePipWindow()
}
@@ -107,7 +114,7 @@ class TvPipMenuTests : TvPipTestBase() {
// PiP menu should contain the Close button
uiDevice.findTvPipMenuCloseButton()
- ?: fail("\"Close PIP\" button should be shown in Pip menu")
+ ?: fail("\"Close PIP\" button should be shown in Pip menu")
// Clicking on the Close button should close the app
uiDevice.clickTvPipMenuCloseButton()
@@ -120,13 +127,15 @@ class TvPipMenuTests : TvPipTestBase() {
// PiP menu should contain the Fullscreen button
uiDevice.findTvPipMenuFullscreenButton()
- ?: fail("\"Full screen\" button should be shown in Pip menu")
+ ?: fail("\"Full screen\" button should be shown in Pip menu")
// Clicking on the fullscreen button should return app to the fullscreen mode.
// Click, wait for the app to go fullscreen
uiDevice.clickTvPipMenuFullscreenButton()
- assertTrue("\"Full screen\" button should open the app fullscreen",
- wait { testApp.ui?.isFullscreen(uiDevice) ?: false })
+ assertTrue(
+ "\"Full screen\" button should open the app fullscreen",
+ wait { testApp.ui?.isFullscreen(uiDevice) ?: false }
+ )
// Close the app
uiDevice.pressBack()
@@ -143,8 +152,10 @@ class TvPipMenuTests : TvPipTestBase() {
// PiP menu should contain the Pause button
uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)
- ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " +
- "playing media session.")
+ ?: fail(
+ "\"Pause\" button should be shown in Pip menu if there is an active " +
+ "playing media session."
+ )
// When we pause media, the button should change from Pause to Play
uiDevice.clickTvPipMenuElementWithDescription(pauseButtonDescription)
@@ -152,8 +163,10 @@ class TvPipMenuTests : TvPipTestBase() {
assertFullscreenAndCloseButtonsAreShown()
// PiP menu should contain the Play button now
uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription)
- ?: fail("\"Play\" button should be shown in Pip menu if there is an active " +
- "paused media session.")
+ ?: fail(
+ "\"Play\" button should be shown in Pip menu if there is an active " +
+ "paused media session."
+ )
testApp.closePipWindow()
}
@@ -165,44 +178,47 @@ class TvPipMenuTests : TvPipTestBase() {
enterPip_openMenu_assertShown()
// PiP menu should contain "No-Op", "Off" and "Clear" buttons...
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_NO_OP)
- ?: fail("\"No-Op\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
- ?: fail("\"Off\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
- ?: fail("\"Clear\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
+ ?: fail("\"No-Op\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
+ ?: fail("\"Off\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
+ ?: fail("\"Clear\" button should be shown in Pip menu")
// ... and should also contain the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
// Invoking the "Off" action should replace it with the "On" action/button and should
// remove the "No-Op" action/button. "Clear" action/button should remain in the menu ...
- uiDevice.waitForTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_ON)
- ?: fail("\"On\" button should be shown in Pip for a corresponding custom action")
- assertNull("\"No-Op\" button should not be shown in Pip menu",
- uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_NO_OP))
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
- ?: fail("\"Clear\" button should be shown in Pip menu")
+ uiDevice.waitForTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_ON)
+ ?: fail("\"On\" button should be shown in Pip for a corresponding custom action")
+ assertNull(
+ "\"No-Op\" button should not be shown in Pip menu",
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
+ )
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
+ ?: fail("\"Clear\" button should be shown in Pip menu")
// ... as well as the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
// Invoking the "Clear" action should remove all the custom actions and their corresponding
// buttons, ...
- uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(
- Components.PipActivity.MENU_ACTION_ON)?.also {
- isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu")
- }
- assertNull("\"Off\" button should not be shown in Pip menu",
- uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_OFF))
- assertNull("\"Clear\" button should not be shown in Pip menu",
- uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_CLEAR))
- assertNull("\"No-Op\" button should not be shown in Pip menu",
- uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_NO_OP))
+ uiDevice
+ .waitUntilTvPipMenuElementWithDescriptionIsGone(ActivityOptions.Pip.MENU_ACTION_ON)
+ ?.also { isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") }
+ assertNull(
+ "\"Off\" button should not be shown in Pip menu",
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
+ )
+ assertNull(
+ "\"Clear\" button should not be shown in Pip menu",
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
+ )
+ assertNull(
+ "\"No-Op\" button should not be shown in Pip menu",
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
+ )
// ... but the menu should still contain the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
@@ -217,26 +233,32 @@ class TvPipMenuTests : TvPipTestBase() {
enterPip_openMenu_assertShown()
// PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions...
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_NO_OP)
- ?: fail("\"No-Op\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
- ?: fail("\"Off\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
- ?: fail("\"Clear\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
+ ?: fail("\"No-Op\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
+ ?: fail("\"Off\" button should be shown in Pip menu")
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
+ ?: fail("\"Clear\" button should be shown in Pip menu")
// ... should also contain the "Full screen" and "Close" buttons, ...
assertFullscreenAndCloseButtonsAreShown()
// ... but should not contain media buttons.
- assertNull("\"Play\" button should not be shown in menu when there are custom actions",
- uiDevice.findTvPipMenuElementWithDescription(playButtonDescription))
- assertNull("\"Pause\" button should not be shown in menu when there are custom actions",
- uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription))
-
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ assertNull(
+ "\"Play\" button should not be shown in menu when there are custom actions",
+ uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)
+ )
+ assertNull(
+ "\"Pause\" button should not be shown in menu when there are custom actions",
+ uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)
+ )
+
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
// Invoking the "Clear" action should remove all the custom actions, which should bring up
// media buttons...
uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription)
- ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " +
- "playing media session.")
+ ?: fail(
+ "\"Pause\" button should be shown in Pip menu if there is an active " +
+ "playing media session."
+ )
// ... while the "Full screen" and "Close" buttons should remain in the menu.
assertFullscreenAndCloseButtonsAreShown()
@@ -244,7 +266,7 @@ class TvPipMenuTests : TvPipTestBase() {
}
private fun enterPip_openMenu_assertShown(): UiObject2 {
- testApp.clickEnterPipButton()
+ testApp.clickEnterPipButton(wmHelper)
// Pressing the Window key should bring up Pip menu
uiDevice.pressWindowKey()
return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown")
@@ -252,8 +274,8 @@ class TvPipMenuTests : TvPipTestBase() {
private fun assertFullscreenAndCloseButtonsAreShown() {
uiDevice.findTvPipMenuCloseButton()
- ?: fail("\"Close PIP\" button should be shown in Pip menu")
+ ?: fail("\"Close PIP\" button should be shown in Pip menu")
uiDevice.findTvPipMenuFullscreenButton()
- ?: fail("\"Full screen\" button should be shown in Pip menu")
+ ?: fail("\"Full screen\" button should be shown in Pip menu")
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
index bcf38d340867..90406c510bad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
@@ -34,8 +34,8 @@ import org.junit.Before
import org.junit.Test
/**
- * Test Pip Notifications on TV.
- * To run this test: `atest WMShellFlickerTests:TvPipNotificationTests`
+ * Test Pip Notifications on TV. To run this test: `atest
+ * WMShellFlickerTests:TvPipNotificationTests`
*/
@RequiresDevice
class TvPipNotificationTests : TvPipTestBase() {
@@ -56,49 +56,58 @@ class TvPipNotificationTests : TvPipTestBase() {
@Test
fun pipNotification_postedAndDismissed() {
testApp.launchViaIntent()
- testApp.clickEnterPipButton()
+ testApp.clickEnterPipButton(wmHelper)
- assertNotNull("Pip notification should have been posted",
- waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) })
+ assertNotNull(
+ "Pip notification should have been posted",
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) }
+ )
testApp.closePipWindow()
- assertTrue("Pip notification should have been dismissed",
- waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) })
+ assertTrue(
+ "Pip notification should have been dismissed",
+ waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) }
+ )
}
@Test
fun pipNotification_closeIntent() {
testApp.launchViaIntent()
- testApp.clickEnterPipButton()
-
- val notification: StatusBarNotification = waitForNotificationToAppear {
- it.isPipNotificationWithTitle(testApp.appName)
- } ?: fail("Pip notification should have been posted")
-
- notification.deleteIntent?.send()
- ?: fail("Pip notification should contain `delete_intent`")
-
- assertTrue("Pip should have closed by sending the `delete_intent`",
- testApp.waitUntilClosed())
- assertTrue("Pip notification should have been dismissed",
- waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) })
+ testApp.clickEnterPipButton(wmHelper)
+
+ val notification: StatusBarNotification =
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) }
+ ?: fail("Pip notification should have been posted")
+
+ notification.deleteIntent?.send() ?: fail("Pip notification should contain `delete_intent`")
+
+ assertTrue(
+ "Pip should have closed by sending the `delete_intent`",
+ testApp.waitUntilClosed()
+ )
+ assertTrue(
+ "Pip notification should have been dismissed",
+ waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) }
+ )
}
@Test
fun pipNotification_menuIntent() {
- testApp.launchViaIntent()
- testApp.clickEnterPipButton()
+ testApp.launchViaIntent(wmHelper)
+ testApp.clickEnterPipButton(wmHelper)
- val notification: StatusBarNotification = waitForNotificationToAppear {
- it.isPipNotificationWithTitle(testApp.appName)
- } ?: fail("Pip notification should have been posted")
+ val notification: StatusBarNotification =
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) }
+ ?: fail("Pip notification should have been posted")
notification.contentIntent?.send()
?: fail("Pip notification should contain `content_intent`")
- assertNotNull("Pip menu should have been shown after sending `content_intent`",
- uiDevice.waitForTvPipMenu())
+ assertNotNull(
+ "Pip menu should have been shown after sending `content_intent`",
+ uiDevice.waitForTvPipMenu()
+ )
uiDevice.pressBack()
testApp.closePipWindow()
@@ -106,41 +115,44 @@ class TvPipNotificationTests : TvPipTestBase() {
@Test
fun pipNotification_mediaSessionTitle_isDisplayed() {
- testApp.launchViaIntent()
+ testApp.launchViaIntent(wmHelper)
// Start media session and to PiP
testApp.clickStartMediaSessionButton()
- testApp.clickEnterPipButton()
+ testApp.clickEnterPipButton(wmHelper)
// Wait for the correct notification to show up...
- waitForNotificationToAppear {
- it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING)
- } ?: fail("Pip notification with media session title should have been posted")
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) }
+ ?: fail("Pip notification with media session title should have been posted")
// ... and make sure "regular" PiP notification is now shown
- assertNull("Regular notification should not have been posted",
- findNotification { it.isPipNotificationWithTitle(testApp.appName) })
+ assertNull(
+ "Regular notification should not have been posted",
+ findNotification { it.isPipNotificationWithTitle(testApp.appName) }
+ )
// Pause the media session. When paused the application updates the title for the media
// session. This change should be reflected in the notification.
testApp.pauseMedia()
// Wait for the "paused" notification to show up...
- waitForNotificationToAppear {
- it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED)
- } ?: fail("Pip notification with media session title should have been posted")
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) }
+ ?: fail("Pip notification with media session title should have been posted")
// ... and make sure "playing" PiP notification is gone
- assertNull("Regular notification should not have been posted",
- findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) })
+ assertNull(
+ "Regular notification should not have been posted",
+ findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) }
+ )
// Now stop the media session, which should revert the title to the "default" one.
testApp.stopMedia()
// Wait for the "regular" notification to show up...
- waitForNotificationToAppear {
- it.isPipNotificationWithTitle(testApp.appName)
- } ?: fail("Pip notification with media session title should have been posted")
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) }
+ ?: fail("Pip notification with media session title should have been posted")
// ... and make sure previous ("paused") notification is gone
- assertNull("Regular notification should not have been posted",
- findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) })
+ assertNull(
+ "Regular notification should not have been posted",
+ findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) }
+ )
testApp.closePipWindow()
}
@@ -170,4 +182,4 @@ private val StatusBarNotification.deleteIntent: PendingIntent?
get() = tvExtensions?.getParcelable("delete_intent")
private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean =
- tag == "TvPip" && title == expectedTitle \ No newline at end of file
+ tag == "TvPip" && title == expectedTitle
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index 9c3b0fa183b6..dc1fe4761757 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -23,8 +23,8 @@ import android.os.SystemClock
import android.view.Surface.ROTATION_0
import android.view.Surface.rotationToString
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.pip.PipTestBase
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assume.assumeTrue
@@ -33,6 +33,7 @@ import org.junit.Before
abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATION_0) {
private val systemUiProcessObserver = SystemUiProcessObserver()
+ protected val wmHelper = WindowManagerStateHelper()
@Before
final override fun televisionSetUp() {
@@ -67,7 +68,8 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO
fun start() {
hasDied = false
uiAutomation.adoptShellPermissionIdentity(
- android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ android.Manifest.permission.SET_ACTIVITY_WATCHER
+ )
activityManager.registerProcessObserver(this)
}
@@ -88,4 +90,4 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO
companion object {
private const val AFTER_TEXT_PROCESS_CHECK_DELAY = 1_000L // 1 sec
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 1c663409b913..b0adbe1d07ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -33,32 +33,31 @@ private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "tv_pip_menu_fullscreen_but
private const val FOCUS_ATTEMPTS = 10
private const val WAIT_TIME_MS = 3_000L
-private val TV_PIP_MENU_SELECTOR =
- By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
+private val TV_PIP_MENU_SELECTOR = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
private val TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR =
- By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID)
+ By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID)
private val TV_PIP_MENU_CLOSE_BUTTON_SELECTOR =
- By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID)
+ By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID)
private val TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR =
- By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID)
+ By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID)
fun UiDevice.waitForTvPipMenu(): UiObject2? =
- wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
+ wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
fun UiDevice.waitForTvPipMenuToClose(): Boolean =
- wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
+ wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
fun UiDevice.findTvPipMenuControls(): UiObject2? =
- findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR)
+ findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR)
fun UiDevice.findTvPipMenuCloseButton(): UiObject2? =
- findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
+ findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? =
- findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
+ findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? =
- findTvPipMenuElement(By.desc(desc))
+ findTvPipMenuElement(By.desc(desc))
private fun UiDevice.findTvPipMenuElement(selector: BySelector): UiObject2? =
findObject(TV_PIP_MENU_SELECTOR)?.findObject(selector)
@@ -70,11 +69,10 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
// descendant and then retrieve the element from the menu and return to the caller of this
// method.
val elementSelector = By.desc(desc)
- val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR)
- .hasDescendant(elementSelector)
+ val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
- ?.findObject(elementSelector)
+ ?.findObject(elementSelector)
}
fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? {
@@ -86,18 +84,17 @@ fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boole
fun UiDevice.clickTvPipMenuCloseButton() {
focusOnAndClickTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) ||
- error("Could not focus on the Close button")
+ error("Could not focus on the Close button")
}
fun UiDevice.clickTvPipMenuFullscreenButton() {
focusOnAndClickTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) ||
- error("Could not focus on the Fullscreen button")
+ error("Could not focus on the Fullscreen button")
}
fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
- focusOnAndClickTvPipMenuElement(By.desc(desc)
- .pkg(SYSTEM_UI_PACKAGE_NAME)) ||
- error("Could not focus on the Pip menu object with \"$desc\" description")
+ focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+ error("Could not focus on the Pip menu object with \"$desc\" description")
// So apparently Accessibility framework on TV is not very reliable and sometimes the state of
// the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
// the "real" state of the "UI tree". It seems, however, that moving focus around the tree
@@ -110,7 +107,8 @@ fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Boolean {
repeat(FOCUS_ATTEMPTS) {
- val element = findTvPipMenuElement(selector)
+ val element =
+ findTvPipMenuElement(selector)
?: error("The Pip Menu element we try to focus on is gone.")
if (element.isFocusedOrHasFocusedChild) {
@@ -119,10 +117,11 @@ private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Bool
}
findTvPipMenuElement(By.focused(true))?.let { focused ->
- if (element.visibleCenter.x < focused.visibleCenter.x)
- pressDPadLeft() else pressDPadRight()
+ if (element.visibleCenter.x < focused.visibleCenter.x) pressDPadLeft()
+ else pressDPadRight()
waitForIdle()
- } ?: error("Pip menu does not contain a focused element")
+ }
+ ?: error("Pip menu does not contain a focused element")
}
return false
@@ -155,9 +154,8 @@ private fun UiDevice.moveFocus() {
fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW)
-fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run {
- height() == uiDevice.displayHeight && width() == uiDevice.displayWidth
-}
+fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean =
+ visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth }
val UiObject2.isFocusedOrHasFocusedChild: Boolean
get() = isFocused || findObject(By.focused(true)) != null
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
new file mode 100644
index 000000000000..65cbea03a044
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.EdgeExtensionComponentMatcher
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.appWindowKeepVisible
+import com.android.wm.shell.flicker.layerKeepVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test copy content from the left to the right side of the split-screen.
+ *
+ * To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class CopyContentInSplit(flicker: FlickerTest) : SplitScreenBase(flicker) {
+ private val textEditApp = SplitScreenUtils.getIme(instrumentation)
+ private val MagnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#")
+ private val PopupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+ transitions {
+ SplitScreenUtils.copyContentInSplit(
+ instrumentation,
+ device,
+ primaryApp,
+ textEditApp
+ )
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() {
+ flicker.appWindowIsVisibleAtStart(primaryApp)
+ flicker.appWindowIsVisibleAtStart(textEditApp)
+ flicker.splitScreenDividerIsVisibleAtStart()
+
+ flicker.appWindowIsVisibleAtEnd(primaryApp)
+ flicker.appWindowIsVisibleAtEnd(textEditApp)
+ flicker.splitScreenDividerIsVisibleAtEnd()
+
+ // The validation of copied text is already done in SplitScreenUtils.copyContentInSplit()
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() = flicker.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+
+ @Presubmit @Test fun primaryAppLayerKeepVisible() = flicker.layerKeepVisible(primaryApp)
+
+ @Presubmit @Test fun textEditAppLayerKeepVisible() = flicker.layerKeepVisible(textEditApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsKeepVisible() =
+ flicker.splitAppLayerBoundsKeepVisible(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun textEditAppBoundsKeepVisible() =
+ flicker.splitAppLayerBoundsKeepVisible(
+ textEditApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+
+ @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+
+ @Presubmit @Test fun textEditAppWindowKeepVisible() = flicker.appWindowKeepVisible(textEditApp)
+
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher.IME_SNAPSHOT,
+ EdgeExtensionComponentMatcher(),
+ MagnifierLayer,
+ PopupWindowLayer
+ )
+ )
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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
new file mode 100644
index 000000000000..d0f02e2bf514
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -0,0 +1,197 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
+import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dismiss split screen by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ if (tapl.isTablet) {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = false,
+ dragToBottom = true
+ )
+ } else {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = true,
+ dragToBottom = true
+ )
+ }
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesInvisible() = flicker.splitScreenDividerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsBecomesInvisible() =
+ flicker.splitAppLayerBoundsBecomesInvisible(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsFullscreenAtEnd() {
+ flicker.assertLayers {
+ this.isVisible(secondaryApp)
+ .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .isInvisible(secondaryApp)
+ .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .isVisible(secondaryApp, isOptional = true)
+ .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT, isOptional = true)
+ .then()
+ .contains(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .invoke("secondaryAppBoundsIsFullscreenAtEnd") {
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
+ it.visibleRegion(secondaryApp).coversExactly(displayBounds)
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesInvisible() = flicker.appWindowBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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
new file mode 100644
index 000000000000..b44b681704ba
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
+import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dismiss split screen by go home.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DismissSplitScreenByGoHome(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ }
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesInvisible() = flicker.splitScreenDividerBecomesInvisible()
+
+ @FlakyTest(bugId = 241525302)
+ @Test
+ fun primaryAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(primaryApp)
+
+ // TODO(b/245472831): Move back to presubmit after shell transitions landing.
+ @FlakyTest(bugId = 245472831)
+ @Test
+ fun secondaryAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(primaryApp)
+
+ // TODO(b/245472831): Move back to presubmit after shell transitions landing.
+ @FlakyTest(bugId = 245472831)
+ @Test
+ fun primaryAppBoundsBecomesInvisible() =
+ flicker.splitAppLayerBoundsBecomesInvisible(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @FlakyTest(bugId = 250530241)
+ @Test
+ fun secondaryAppBoundsBecomesInvisible() =
+ flicker.splitAppLayerBoundsBecomesInvisible(
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesInvisible() = flicker.appWindowBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesInvisible() = flicker.appWindowBecomesInvisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 251268711)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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
new file mode 100644
index 000000000000..fcdad960107f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -0,0 +1,214 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.appWindowKeepVisible
+import com.android.wm.shell.flicker.layerKeepVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsChanges
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test resize split by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DragDividerToResize`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DragDividerToResize(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
+ }
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() {
+ flicker.appWindowIsVisibleAtStart(primaryApp)
+ flicker.appWindowIsVisibleAtStart(secondaryApp)
+ flicker.splitScreenDividerIsVisibleAtStart()
+
+ flicker.appWindowIsVisibleAtEnd(primaryApp)
+ flicker.appWindowIsVisibleAtEnd(secondaryApp)
+ flicker.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() = flicker.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerKeepVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.layerKeepVisible(primaryApp)
+ }
+
+ @FlakyTest(bugId = 263213649)
+ @Test
+ fun primaryAppLayerKeepVisible_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerKeepVisible(primaryApp)
+ }
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerVisibilityChanges() {
+ flicker.assertLayers {
+ this.isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ }
+ }
+
+ @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsChanges() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.splitAppLayerBoundsChanges(
+ primaryApp,
+ landscapePosLeft = true,
+ portraitPosTop = false
+ )
+ }
+
+ @FlakyTest(bugId = 263213649)
+ @Test
+ fun primaryAppBoundsChanges_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.splitAppLayerBoundsChanges(
+ primaryApp,
+ landscapePosLeft = true,
+ portraitPosTop = false
+ )
+ }
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsChanges() =
+ flicker.splitAppLayerBoundsChanges(
+ secondaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = true
+ )
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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 702710caded7..4e36c367f226 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,22 +16,25 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -41,8 +44,8 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test enter split screen by dragging app icon from all apps.
- * This test is only for large screen devices.
+ * Test enter split screen by dragging app icon from all apps. This test is only for large screen
+ * devices.
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
*/
@@ -50,74 +53,141 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class EnterSplitScreenByDragFromAllApps(
- testSpec: FlickerTestParameter
-) : SplitScreenBase(testSpec) {
+class EnterSplitScreenByDragFromAllApps(flicker: FlickerTest) : SplitScreenBase(flicker) {
@Before
- open fun before() {
- Assume.assumeTrue(taplInstrumentation.isTablet)
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
}
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- eachRun {
- taplInstrumentation.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
- taplInstrumentation.launchedAppState.taskbar
+ tapl.launchedAppState.taskbar
.openAllApps()
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.component.packageName,
- primaryApp.component.packageName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
+ @IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
@Presubmit
@Test
- fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.splitScreenDividerBecomesVisible()
+ }
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
@Presubmit
@Test
- fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component)
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+ }
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
@Presubmit
@Test
- fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- testSpec.endRotation, primaryApp.component, false /* splitLeftTop */)
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(secondaryApp)
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */)
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
@Presubmit
@Test
- fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+ fun secondaryAppBoundsBecomesVisible() =
+ flicker.splitAppLayerBoundsBecomesVisibleByDrag(secondaryApp)
@Presubmit
@Test
- fun secondaryAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(secondaryApp.component)
+ fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes =
- listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
}
}
}
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 7323d992ecd4..5d37e858c15f 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,23 +16,24 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -42,8 +43,8 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test enter split screen by dragging app icon from notification.
- * This test is only for large screen devices.
+ * Test enter split screen by dragging app icon from notification. This test is only for large
+ * screen devices.
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
*/
@@ -51,88 +52,163 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class EnterSplitScreenByDragFromNotification(
- testSpec: FlickerTestParameter
-) : SplitScreenBase(testSpec) {
+class EnterSplitScreenByDragFromNotification(flicker: FlickerTest) : SplitScreenBase(flicker) {
- private val sendNotificationApp = SplitScreenHelper.getSendNotification(instrumentation)
+ private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
@Before
fun before() {
- Assume.assumeTrue(taplInstrumentation.isTablet)
+ Assume.assumeTrue(tapl.isTablet)
}
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- eachRun {
- // Send a notification
- sendNotificationApp.launchViaIntent(wmHelper)
- val sendNotification = device.wait(
- Until.findObject(By.text("Send Notification")),
- SplitScreenHelper.TIMEOUT_MS
- )
- sendNotification?.click() ?: error("Send notification button not found")
-
- taplInstrumentation.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ sendNotificationApp.postNotification(wmHelper)
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
- SplitScreenHelper.dragFromNotificationToSplit(instrumentation, device, wmHelper)
- }
- teardown {
- eachRun {
- sendNotificationApp.exit(wmHelper)
- }
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
}
+ teardown { sendNotificationApp.exit(wmHelper) }
}
+ @IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
@Presubmit
@Test
- fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.splitScreenDividerBecomesVisible()
+ }
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
@Presubmit
@Test
- fun secondaryAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(sendNotificationApp.component)
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+ }
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
@Presubmit
@Test
- fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- testSpec.endRotation, primaryApp.component, false /* splitLeftTop */
- )
+ fun secondaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.assertLayers {
+ this.isInvisible(sendNotificationApp)
+ .then()
+ .isVisible(sendNotificationApp)
+ .then()
+ .isInvisible(sendNotificationApp)
+ .then()
+ .isVisible(sendNotificationApp)
+ }
+ }
+ // TODO(b/245472831): Align to legacy transition after shell transition ready.
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- testSpec.endRotation, sendNotificationApp.component, true /* splitLeftTop */
- )
+ fun secondaryAppLayerBecomesVisible_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(sendNotificationApp)
+ }
@Presubmit
@Test
- fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
@Presubmit
@Test
- fun secondaryAppWindowIsVisibleAtEnd() =
- testSpec.appWindowIsVisibleAtEnd(sendNotificationApp.component)
+ fun secondaryAppBoundsBecomesVisible() =
+ flicker.splitAppLayerBoundsBecomesVisibleByDrag(sendNotificationApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(sendNotificationApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes =
- listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
)
}
}
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
new file mode 100644
index 000000000000..af63f7c26a8c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen by dragging a shortcut. This test is only for large screen devices.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromShortcut`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenByDragFromShortcut(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .openDeepShortcutMenu()
+ .getMenuItem("Split Screen Secondary Activity")
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() =
+ flicker.splitAppLayerBoundsBecomesVisibleByDrag(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() {
+ flicker.assertWm {
+ this.notContains(secondaryApp)
+ .then()
+ .isAppWindowInvisible(secondaryApp, isOptional = true)
+ .then()
+ .isAppWindowVisible(secondaryApp)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
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 05c6e24ee89d..795a2c4f43ba 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,22 +16,25 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -41,8 +44,8 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test enter split screen by dragging app icon from taskbar.
- * This test is only for large screen devices.
+ * Test enter split screen by dragging app icon from taskbar. This test is only for large screen
+ * devices.
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
*/
@@ -50,79 +53,159 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class EnterSplitScreenByDragFromTaskbar(
- testSpec: FlickerTestParameter
-) : SplitScreenBase(testSpec) {
+class EnterSplitScreenByDragFromTaskbar(flicker: FlickerTest) : SplitScreenBase(flicker) {
@Before
fun before() {
- Assume.assumeTrue(taplInstrumentation.isTablet)
+ Assume.assumeTrue(tapl.isTablet)
}
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- eachRun {
- taplInstrumentation.goHome()
- SplitScreenHelper.createShortcutOnHotseatIfNotExist(
- taplInstrumentation, secondaryApp.appName
- )
- primaryApp.launchViaIntent(wmHelper)
- }
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
- taplInstrumentation.launchedAppState.taskbar
+ tapl.launchedAppState.taskbar
.getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(
- secondaryApp.component.packageName,
- primaryApp.component.packageName
- )
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
+ @IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
@Presubmit
@Test
- fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.splitScreenDividerBecomesVisible()
+ }
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
@Presubmit
@Test
- fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component)
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+ }
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
@Presubmit
@Test
- fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- testSpec.endRotation, primaryApp.component, false /* splitLeftTop */
- )
+ fun secondaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.assertLayers {
+ this.isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ }
+ }
+
+ // TODO(b/245472831): Align to legacy transition after shell transition ready.
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(secondaryApp)
+ }
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */
- )
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
@Presubmit
@Test
- fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+ fun secondaryAppBoundsBecomesVisible() =
+ flicker.splitAppLayerBoundsBecomesVisibleByDrag(secondaryApp)
@Presubmit
@Test
- fun secondaryAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(secondaryApp.component)
+ fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedNavigationModes =
- listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
)
}
}
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
new file mode 100644
index 000000000000..c09ca914caff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 The Android 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.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
+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 enter split screen from Overview.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenFromOverview(flicker: FlickerTest) : SplitScreenBase(flicker) {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ transitions {
+ SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.splitAppLayerBoundsBecomesVisible(
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+ }
+
+ @FlakyTest(bugId = 244407465)
+ @Test
+ fun secondaryAppBoundsBecomesVisible_shellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.splitAppLayerBoundsBecomesVisible(
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 251269324)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 252736515)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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/SplitScreenBase.kt
index 52c2daf96a3c..8c0a303189e1 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/SplitScreenBase.kt
@@ -16,44 +16,29 @@
package com.android.wm.shell.flicker.splitscreen
-import android.app.Instrumentation
import android.content.Context
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.BaseTest
-abstract class SplitScreenBase(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val taplInstrumentation = LauncherInstrumentation()
+abstract class SplitScreenBase(flicker: FlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
- protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
- protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+ protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition(this)
- }
- }
-
- protected open val transition: FlickerBuilder.() -> Unit
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- taplInstrumentation.setEnableRotation(true)
- setRotation(testSpec.startRotation)
- taplInstrumentation.setExpectedRotation(testSpec.startRotation)
- }
+ tapl.setEnableRotation(true)
+ setRotation(flicker.scenario.startRotation)
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
}
teardown {
- eachRun {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- }
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
new file mode 100644
index 000000000000..f3927d405467
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -0,0 +1,383 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.os.SystemClock
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.NotificationAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.IComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+import java.util.Collections
+import org.junit.Assert.assertNotNull
+
+internal object SplitScreenUtils {
+ private const val TIMEOUT_MS = 3_000L
+ private const val DRAG_DURATION_MS = 1_000L
+ private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ private const val DIVIDER_BAR = "docked_divider_handle"
+ private const val OVERVIEW_SNAPSHOT = "snapshot"
+ private const val GESTURE_STEP_MS = 16L
+ private val LONG_PRESS_TIME_MS = ViewConfiguration.getLongPressTimeout() * 2L
+ private val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
+
+ private val notificationScrollerSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
+ private val notificationContentSelector: BySelector
+ get() = By.text("Flicker Test Notification")
+ private val dividerBarSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
+ private val overviewSnapshotSelector: BySelector
+ get() = By.res(LAUNCHER_UI_PACKAGE_NAME, OVERVIEW_SNAPSHOT)
+
+ fun getPrimary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getSecondary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Secondary.LABEL,
+ ActivityOptions.SplitScreen.Secondary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getNonResizeable(instrumentation: Instrumentation): NonResizeableAppHelper =
+ NonResizeableAppHelper(instrumentation)
+
+ fun getSendNotification(instrumentation: Instrumentation): NotificationAppHelper =
+ NotificationAppHelper(instrumentation)
+
+ fun getIme(instrumentation: Instrumentation): ImeAppHelper = ImeAppHelper(instrumentation)
+
+ fun waitForSplitComplete(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: IComponentMatcher,
+ secondaryApp: IComponentMatcher,
+ ) {
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(primaryApp)
+ .withWindowSurfaceAppeared(secondaryApp)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
+ }
+
+ fun enterSplit(
+ wmHelper: WindowManagerStateHelper,
+ tapl: LauncherInstrumentation,
+ device: UiDevice,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ splitFromOverview(tapl, device)
+ waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+ // 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.
+ if (tapl.isTablet) {
+ // 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 snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
+ if (snapshots == null || snapshots.size < 1) {
+ error("Fail to find a overview snapshot to split.")
+ }
+
+ // Find the second task in the upper right corner in split select mode by sorting
+ // 'left' in descending order and 'top' in ascending order.
+ Collections.sort(
+ snapshots,
+ { t1: UiObject2, t2: UiObject2 ->
+ t2.getVisibleBounds().left - t1.getVisibleBounds().left
+ }
+ )
+ Collections.sort(
+ snapshots,
+ { t1: UiObject2, t2: UiObject2 ->
+ t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ }
+ )
+ snapshots[0].click()
+ } else {
+ tapl.workspace
+ .switchToOverview()
+ .currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun dragFromNotificationToSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+
+ // Pull down the notifications
+ device.swipe(
+ displayBounds.centerX(),
+ 5,
+ displayBounds.centerX(),
+ displayBounds.bottom,
+ 50 /* steps */
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+
+ // Find the target notification
+ val notificationScroller =
+ device.wait(Until.findObject(notificationScrollerSelector), TIMEOUT_MS)
+ ?: error("Unable to find view $notificationScrollerSelector")
+ var notificationContent = notificationScroller.findObject(notificationContentSelector)
+
+ while (notificationContent == null) {
+ device.swipe(
+ displayBounds.centerX(),
+ displayBounds.centerY(),
+ displayBounds.centerX(),
+ displayBounds.centerY() - 150,
+ 20 /* steps */
+ )
+ notificationContent = notificationScroller.findObject(notificationContentSelector)
+ }
+
+ // Drag to split
+ val dragStart = notificationContent.visibleCenter
+ val dragMiddle = Point(dragStart.x + 50, dragStart.y)
+ val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
+ val downTime = SystemClock.uptimeMillis()
+
+ touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, dragStart)
+ // It needs a horizontal movement to trigger the drag
+ touchMove(
+ instrumentation,
+ downTime,
+ SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS,
+ dragStart,
+ dragMiddle
+ )
+ touchMove(
+ instrumentation,
+ downTime,
+ SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS,
+ dragMiddle,
+ dragEnd
+ )
+ // Wait for a while to start splitting
+ SystemClock.sleep(TIMEOUT_MS)
+ touch(
+ instrumentation,
+ MotionEvent.ACTION_UP,
+ downTime,
+ SystemClock.uptimeMillis(),
+ GESTURE_STEP_MS,
+ dragEnd
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun touch(
+ instrumentation: Instrumentation,
+ action: Int,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ point: Point
+ ) {
+ val motionEvent =
+ MotionEvent.obtain(downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0)
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
+ motionEvent.recycle()
+ SystemClock.sleep(duration)
+ }
+
+ fun touchMove(
+ instrumentation: Instrumentation,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ from: Point,
+ to: Point
+ ) {
+ val steps: Long = duration / GESTURE_STEP_MS
+ var currentTime = eventTime
+ var currentX = from.x.toFloat()
+ var currentY = from.y.toFloat()
+ val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
+ val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
+
+ for (i in 1..steps) {
+ val motionMove =
+ MotionEvent.obtain(
+ downTime,
+ currentTime,
+ MotionEvent.ACTION_MOVE,
+ currentX,
+ currentY,
+ 0
+ )
+ motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionMove, true)
+ motionMove.recycle()
+
+ currentTime += GESTURE_STEP_MS
+ if (i == steps - 1) {
+ currentX = to.x.toFloat()
+ currentY = to.y.toFloat()
+ } else {
+ currentX += stepX
+ currentY += stepY
+ }
+ SystemClock.sleep(GESTURE_STEP_MS)
+ }
+ }
+
+ fun createShortcutOnHotseatIfNotExist(tapl: LauncherInstrumentation, appName: String) {
+ tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0))
+ val allApps = tapl.workspace.switchToAllApps()
+ allApps.freeze()
+ try {
+ allApps.getAppIcon(appName).dragToHotseat(0)
+ } finally {
+ allApps.unfreeze()
+ }
+ }
+
+ fun dragDividerToResizeAndWait(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3))
+
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER)
+ .waitForAndVerify()
+ }
+
+ fun dragDividerToDismissSplit(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper,
+ dragToRight: Boolean,
+ dragToBottom: Boolean
+ ) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(
+ Point(
+ if (dragToRight) {
+ displayBounds.width * 4 / 5
+ } else {
+ displayBounds.width * 1 / 5
+ },
+ if (dragToBottom) {
+ displayBounds.height * 4 / 5
+ } else {
+ displayBounds.height * 1 / 5
+ }
+ )
+ )
+ }
+
+ fun doubleTapDividerToSwitch(device: UiDevice) {
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ val interval =
+ (ViewConfiguration.getDoubleTapTimeout() + ViewConfiguration.getDoubleTapMinTime()) / 2
+ dividerBar.click()
+ SystemClock.sleep(interval.toLong())
+ dividerBar.click()
+ }
+
+ fun copyContentInSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ sourceApp: IComponentNameMatcher,
+ destinationApp: IComponentNameMatcher,
+ ) {
+ // Copy text from sourceApp
+ val textView =
+ device.wait(
+ Until.findObject(By.res(sourceApp.packageName, "SplitScreenTest")),
+ TIMEOUT_MS
+ )
+ assertNotNull("Unable to find the TextView", textView)
+ textView.click(LONG_PRESS_TIME_MS)
+
+ val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
+ assertNotNull("Unable to find the copy button", copyBtn)
+ copyBtn.click()
+
+ // Paste text to destinationApp
+ val editText =
+ device.wait(
+ Until.findObject(By.res(destinationApp.packageName, "plain_text_input")),
+ TIMEOUT_MS
+ )
+ assertNotNull("Unable to find the EditText", editText)
+ editText.click(LONG_PRESS_TIME_MS)
+
+ val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
+ assertNotNull("Unable to find the paste button", pasteBtn)
+ pasteBtn.click()
+
+ // Verify text
+ if (!textView.text.contentEquals(editText.text)) {
+ error("Fail to copy content in split")
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 000000000000..09568b291830
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,259 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.layerKeepVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test double tap the divider bar to switch the two apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
+ }
+ }
+
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appWindowsSwitched") {
+ val primaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ if (isLandscape(flicker.scenario.endRotation)) {
+ return@add if (flicker.scenario.isTablet) {
+ secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+ } else {
+ primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ }
+ } else {
+ return@add if (flicker.scenario.isTablet) {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ } else {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appLayersSwitched") {
+ val primaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+ val secondaryVisibleRegion =
+ secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+ if (isLandscape(flicker.scenario.endRotation)) {
+ return@add if (flicker.scenario.isTablet) {
+ secondaryVisibleRegion.right <= primaryVisibleRegion.left
+ } else {
+ primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ }
+ } else {
+ return@add if (flicker.scenario.isTablet) {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ } else {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun isLandscape(rotation: PlatformConsts.Rotation): Boolean {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return displayBounds.width > displayBounds.height
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() {
+ flicker.appWindowIsVisibleAtStart(primaryApp)
+ flicker.appWindowIsVisibleAtStart(secondaryApp)
+ flicker.splitScreenDividerIsVisibleAtStart()
+
+ flicker.appWindowIsVisibleAtEnd(primaryApp)
+ flicker.appWindowIsVisibleAtEnd(secondaryApp)
+ flicker.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() = flicker.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+
+ @Presubmit @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..940e0e93d524
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
+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 to split pair from another app.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchBackToSplitFromAnotherApp(flicker: FlickerTest) : SplitScreenBase(flicker) {
+ val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(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
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @FlakyTest @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..85812c420f3b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
+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 to split pair from home.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchBackToSplitFromHome(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(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
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @FlakyTest @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 252736515)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..7c62433d8905
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.server.wm.traces.common.service.PlatformConsts
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test switch back to split pair from recent.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchBackToSplitFromRecent(flicker: FlickerTest) : SplitScreenBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ tapl.workspace.switchToOverview().currentTask.open()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(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
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..193ab98cf191
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -0,0 +1,241 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerBuilder
+import com.android.server.wm.flicker.FlickerTest
+import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsInvisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsSnapToDivider
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+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 WMShellFlickerTests:SwitchBetweenSplitPairs`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SwitchBetweenSplitPairs(flicker: FlickerTest) : SplitScreenBase(flicker) {
+ private val thirdApp = SplitScreenUtils.getIme(instrumentation)
+ private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ teardown {
+ thirdApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ }
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() {
+ flicker.appWindowIsVisibleAtStart(thirdApp)
+ flicker.appWindowIsVisibleAtStart(fourthApp)
+ flicker.splitScreenDividerIsVisibleAtStart()
+
+ flicker.appWindowIsVisibleAtEnd(primaryApp)
+ flicker.appWindowIsVisibleAtEnd(secondaryApp)
+ flicker.appWindowIsInvisibleAtEnd(thirdApp)
+ flicker.appWindowIsInvisibleAtEnd(fourthApp)
+ flicker.splitScreenDividerIsVisibleAtEnd()
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerInvisibleAtMiddle() =
+ flicker.assertLayers {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+
+ @FlakyTest(bugId = 247095572)
+ @Test
+ fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+
+ @FlakyTest(bugId = 247095572)
+ @Test
+ fun secondaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(secondaryApp)
+
+ @FlakyTest(bugId = 247095572)
+ @Test
+ fun thirdAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(thirdApp)
+
+ @FlakyTest(bugId = 247095572)
+ @Test
+ fun fourthAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(fourthApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() =
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
+
+ @Presubmit
+ @Test
+ fun thirdAppBoundsIsVisibleAtBegin() =
+ flicker.assertLayersStart {
+ this.splitAppLayerBoundsSnapToDivider(
+ thirdApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false,
+ flicker.scenario.startRotation
+ )
+ }
+
+ @Presubmit
+ @Test
+ fun fourthAppBoundsIsVisibleAtBegin() =
+ flicker.assertLayersStart {
+ this.splitAppLayerBoundsSnapToDivider(
+ fourthApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true,
+ flicker.scenario.startRotation
+ )
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun thirdAppWindowBecomesVisible() = flicker.appWindowBecomesInvisible(thirdApp)
+
+ @Presubmit
+ @Test
+ fun fourthAppWindowBecomesVisible() = flicker.appWindowBecomesInvisible(fourthApp)
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 251268711)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
deleted file mode 100644
index ea606df1536d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "WMShellFlickerTestApp",
- srcs: ["**/*.java"],
- sdk_version: "current",
- test_suites: ["device-tests"],
-}
-
-java_library {
- name: "wmshell-flicker-test-components",
- srcs: ["src/**/Components.java"],
- sdk_version: "test_current",
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
deleted file mode 100644
index bc0b0b6292b4..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-<?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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.testapp">
-
- <uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
- <application android:allowBackup="false"
- android:supportsRtl="true">
- <activity android:name=".FixedActivity"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:launchMode="singleTop"
- android:theme="@style/CutoutShortEdges"
- android:label="FixedApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity android:name=".PipActivity"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
- android:theme="@style/CutoutShortEdges"
- android:launchMode="singleTop"
- android:label="PipApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".ImeActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeApp"
- android:launchMode="singleTop"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SplitScreenActivity"
- android:resizeableActivity="true"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SplitScreenPrimaryApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SplitScreenSecondaryActivity"
- android:resizeableActivity="true"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SplitScreenSecondaryApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SendNotificationActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SendNotificationActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SendNotificationApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".NonResizeableActivity"
- android:resizeableActivity="false"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="NonResizeableApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SimpleActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SimpleApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity
- android:name=".LaunchBubbleActivity"
- android:label="LaunchBubbleApp"
- android:exported="true"
- android:theme="@style/CutoutShortEdges"
- android:launchMode="singleTop">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity
- android:name=".BubbleActivity"
- android:label="BubbleApp"
- android:exported="false"
- android:theme="@style/CutoutShortEdges"
- android:resizeableActivity="true" />
- </application>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png
deleted file mode 100644
index d424a17b4157..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml
deleted file mode 100644
index b43f31da748d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M7.2,14.4m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M14.8,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M15.2,8.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0"/>
-</vector>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml
deleted file mode 100644
index 0e8c7a0fe64a..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<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="M12,4c-4.97,0 -9,3.58 -9,8c0,1.53 0.49,2.97 1.33,4.18c0.12,0.18 0.2,0.46 0.1,0.66c-0.33,0.68 -0.79,1.52 -1.38,2.39c-0.12,0.17 0.01,0.41 0.21,0.39c0.63,-0.05 1.86,-0.26 3.38,-0.91c0.17,-0.07 0.36,-0.06 0.52,0.03C8.55,19.54 10.21,20 12,20c4.97,0 9,-3.58 9,-8S16.97,4 12,4zM16.94,11.63l-3.29,3.29c-0.13,0.13 -0.34,0.04 -0.34,-0.14v-1.57c0,-0.11 -0.1,-0.21 -0.21,-0.2c-2.19,0.06 -3.65,0.65 -5.14,1.95c-0.15,0.13 -0.38,0 -0.33,-0.19c0.7,-2.57 2.9,-4.57 5.5,-4.75c0.1,-0.01 0.18,-0.09 0.18,-0.19V8.2c0,-0.18 0.22,-0.27 0.34,-0.14l3.29,3.29C17.02,11.43 17.02,11.55 16.94,11.63z"
- android:fillColor="#000000"
- android:fillType="evenOdd"/>
-</vector>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml
deleted file mode 100644
index f8b0ca3da26e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <Button
- android:id="@+id/button_finish"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginStart="8dp"
- android:text="Finish" />
- <Button
- android:id="@+id/button_new_task"
- android:layout_width="wrap_content"
- android:layout_height="46dp"
- android:layout_marginStart="8dp"
- android:text="New Task" />
- <Button
- android:id="@+id/button_new_bubble"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:text="New Bubble" />
-
- <Button
- android:id="@+id/button_activity_for_result"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:layout_marginStart="8dp"
- android:text="Activity For Result" />
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
deleted file mode 100644
index 4708cfd48381..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2018 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"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusableInTouchMode="true"
- android:background="@android:color/holo_green_light">
- <EditText android:id="@+id/plain_text_input"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:inputType="text"/>
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml
deleted file mode 100644
index f23c46455c63..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/black">
-
- <Button
- android:id="@+id/button_create"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:text="Add Bubble" />
-
- <Button
- android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/button_create"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="20dp"
- android:text="Cancel Bubble" />
-
- <Button
- android:id="@+id/button_cancel_all"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/button_cancel"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="20dp"
- android:text="Cancel All Bubble" />
-</RelativeLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
deleted file mode 100644
index 45d5917f86d6..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/holo_orange_light">
-
- <TextView
- android:id="@+id/NonResizeableTest"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center_vertical|center_horizontal"
- android:text="NonResizeableActivity"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
deleted file mode 100644
index 8d59b567e59b..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/black">
-
- <Button
- android:id="@+id/button_send_notification"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:text="Send Notification" />
-</RelativeLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
deleted file mode 100644
index 229098313afa..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-<?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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/holo_blue_bright">
-
- <!-- All the buttons (and other clickable elements) should be arranged in a way so that it is
- possible to "cycle" over all them by clicking on the D-Pad DOWN button. The way we do it
- here is by arranging them this vertical LL and by relying on the nextFocusDown attribute
- where things are arranged differently and to circle back up to the top once we reach the
- bottom. -->
-
- <Button
- android:id="@+id/enter_pip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Enter PIP"
- android:onClick="enterPip"/>
-
- <CheckBox
- android:id="@+id/with_custom_actions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="With custom actions"/>
-
- <RadioGroup
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:checkedButton="@id/enter_pip_on_leave_disabled">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Enter PiP on home press"/>
-
- <RadioButton
- android:id="@+id/enter_pip_on_leave_disabled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Disabled"
- android:onClick="onAutoPipSelected"/>
-
- <RadioButton
- android:id="@+id/enter_pip_on_leave_manual"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Via code behind"
- android:onClick="onAutoPipSelected"/>
-
- <RadioButton
- android:id="@+id/enter_pip_on_leave_autoenter"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Auto-enter PiP"
- android:onClick="onAutoPipSelected"/>
- </RadioGroup>
-
- <RadioGroup
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:checkedButton="@id/ratio_default">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Ratio"/>
-
- <RadioButton
- android:id="@+id/ratio_default"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Default"
- android:onClick="onRatioSelected"/>
-
- <RadioButton
- android:id="@+id/ratio_square"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Square [1:1]"
- android:onClick="onRatioSelected"/>
-
- <RadioButton
- android:id="@+id/ratio_wide"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Wide [2:1]"
- android:onClick="onRatioSelected"/>
-
- <RadioButton
- android:id="@+id/ratio_tall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Tall [1:2]"
- android:onClick="onRatioSelected"/>
- </RadioGroup>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Media Session"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <Button
- android:id="@+id/media_session_start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:nextFocusDown="@id/media_session_stop"
- android:text="Start"/>
-
- <Button
- android:id="@+id/media_session_stop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:nextFocusDown="@id/enter_pip"
- android:text="Stop"/>
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml
deleted file mode 100644
index 5d94e5177dcc..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2018 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"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/holo_orange_light">
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
deleted file mode 100644
index 84789f5a6c02..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/holo_green_light">
-
- <TextView
- android:id="@+id/SplitScreenTest"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center_vertical|center_horizontal"
- android:text="PrimaryActivity"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
deleted file mode 100644
index 674bb70ad01e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/holo_blue_light">
-
- <TextView
- android:id="@+id/SplitScreenTest"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center_vertical|center_horizontal"
- android:text="SecondaryActivity"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
deleted file mode 100644
index 23b51cc06f04..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
- -->
-
-<resources>
- <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:windowBackground">@android:color/darker_gray</item>
- </style>
-
- <style name="CutoutDefault" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">default</item>
- </style>
-
- <style name="CutoutShortEdges" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
- </style>
-
- <style name="CutoutNever" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">never</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java
deleted file mode 100644
index bc3bc75ab903..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.Toast;
-
-public class BubbleActivity extends Activity {
- private int mNotifId = 0;
-
- public BubbleActivity() {
- super();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Intent intent = getIntent();
- if (intent != null) {
- mNotifId = intent.getIntExtra(BubbleHelper.EXTRA_BUBBLE_NOTIF_ID, -1);
- } else {
- mNotifId = -1;
- }
-
- setContentView(R.layout.activity_bubble);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- String result = resultCode == Activity.RESULT_OK ? "OK" : "CANCELLED";
- Toast.makeText(this, "Activity result: " + result, Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java
deleted file mode 100644
index 6cd93eff2803..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Point;
-import android.graphics.drawable.Icon;
-import android.os.SystemClock;
-import android.service.notification.StatusBarNotification;
-import android.view.WindowManager;
-
-import java.util.HashMap;
-
-public class BubbleHelper {
-
- static final String EXTRA_BUBBLE_NOTIF_ID = "EXTRA_BUBBLE_NOTIF_ID";
- static final String CHANNEL_ID = "bubbles";
- static final String CHANNEL_NAME = "Bubbles";
- static final int DEFAULT_HEIGHT_DP = 300;
-
- private static BubbleHelper sInstance;
-
- private final Context mContext;
- private NotificationManager mNotificationManager;
- private float mDisplayHeight;
-
- private HashMap<Integer, BubbleInfo> mBubbleMap = new HashMap<>();
-
- private int mNextNotifyId = 0;
- private int mColourIndex = 0;
-
- public static class BubbleInfo {
- public int id;
- public int height;
- public Icon icon;
-
- public BubbleInfo(int id, int height, Icon icon) {
- this.id = id;
- this.height = height;
- this.icon = icon;
- }
- }
-
- public static BubbleHelper getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new BubbleHelper(context);
- }
- return sInstance;
- }
-
- private BubbleHelper(Context context) {
- mContext = context;
- mNotificationManager = context.getSystemService(NotificationManager.class);
-
- NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
- NotificationManager.IMPORTANCE_DEFAULT);
- channel.setDescription("Channel that posts bubbles");
- channel.setAllowBubbles(true);
- mNotificationManager.createNotificationChannel(channel);
-
- Point p = new Point();
- WindowManager wm = context.getSystemService(WindowManager.class);
- wm.getDefaultDisplay().getRealSize(p);
- mDisplayHeight = p.y;
-
- }
-
- private int getNextNotifyId() {
- int id = mNextNotifyId;
- mNextNotifyId++;
- return id;
- }
-
- private Icon getIcon() {
- return Icon.createWithResource(mContext, R.drawable.bg);
- }
-
- public int addNewBubble(boolean autoExpand, boolean suppressNotif) {
- int id = getNextNotifyId();
- BubbleInfo info = new BubbleInfo(id, DEFAULT_HEIGHT_DP, getIcon());
- mBubbleMap.put(info.id, info);
-
- Notification.BubbleMetadata data = getBubbleBuilder(info)
- .setSuppressNotification(suppressNotif)
- .setAutoExpandBubble(false)
- .build();
- Notification notification = getNotificationBuilder(info.id)
- .setBubbleMetadata(data).build();
-
- mNotificationManager.notify(info.id, notification);
- return info.id;
- }
-
- private Notification.Builder getNotificationBuilder(int id) {
- Person chatBot = new Person.Builder()
- .setBot(true)
- .setName("BubbleChat")
- .setImportant(true)
- .build();
- String shortcutId = "BubbleChat";
- return new Notification.Builder(mContext, CHANNEL_ID)
- .setChannelId(CHANNEL_ID)
- .setShortcutId(shortcutId)
- .setContentTitle("BubbleChat")
- .setContentIntent(PendingIntent.getActivity(mContext, 0,
- new Intent(mContext, LaunchBubbleActivity.class),
- PendingIntent.FLAG_UPDATE_CURRENT))
- .setStyle(new Notification.MessagingStyle(chatBot)
- .setConversationTitle("BubbleChat")
- .addMessage("BubbleChat",
- SystemClock.currentThreadTimeMillis() - 300000, chatBot)
- .addMessage("Is it me, " + id + ", you're looking for?",
- SystemClock.currentThreadTimeMillis(), chatBot)
- )
- .setSmallIcon(R.drawable.ic_bubble);
- }
-
- private Notification.BubbleMetadata.Builder getBubbleBuilder(BubbleInfo info) {
- Intent target = new Intent(mContext, BubbleActivity.class);
- target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
- PendingIntent.FLAG_UPDATE_CURRENT);
-
- return new Notification.BubbleMetadata.Builder()
- .setIntent(bubbleIntent)
- .setIcon(info.icon)
- .setDesiredHeight(info.height);
- }
-
- public void cancel(int id) {
- mNotificationManager.cancel(id);
- }
-
- public void cancelAll() {
- mNotificationManager.cancelAll();
- }
-
- public void cancelLast() {
- StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
- if (activeNotifications.length > 0) {
- mNotificationManager.cancel(
- activeNotifications[activeNotifications.length - 1].getId());
- }
- }
-
- public void cancelFirst() {
- StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
- if (activeNotifications.length > 0) {
- mNotificationManager.cancel(activeNotifications[0].getId());
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
deleted file mode 100644
index a2b580da5898..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import android.content.ComponentName;
-
-public class Components {
- public static final String PACKAGE_NAME = "com.android.wm.shell.flicker.testapp";
-
- public static class SimpleActivity {
- public static final String LABEL = "SimpleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SimpleActivity");
- }
-
- public static class FixedActivity {
- public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
- public static final String LABEL = "FixedApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".FixedActivity");
- }
-
- public static class NonResizeableActivity {
- public static final String LABEL = "NonResizeableApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".NonResizeableActivity");
- }
-
- public static class PipActivity {
- // Test App > Pip Activity
- public static final String LABEL = "PipApp";
- public static final String MENU_ACTION_NO_OP = "No-Op";
- public static final String MENU_ACTION_ON = "On";
- public static final String MENU_ACTION_OFF = "Off";
- public static final String MENU_ACTION_CLEAR = "Clear";
-
- // Intent action that this activity dynamically registers to enter picture-in-picture
- public static final String ACTION_ENTER_PIP = PACKAGE_NAME + ".PipActivity.ENTER_PIP";
- // Intent action that this activity dynamically registers to set requested orientation.
- // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
- public static final String ACTION_SET_REQUESTED_ORIENTATION =
- PACKAGE_NAME + ".PipActivity.SET_REQUESTED_ORIENTATION";
-
- // Calls enterPictureInPicture() on creation
- public static final String EXTRA_ENTER_PIP = "enter_pip";
- // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
- public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
- // Adds a click listener to finish this activity when it is clicked
- public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".PipActivity");
- }
-
- public static class ImeActivity {
- public static final String LABEL = "ImeApp";
- public static final String ACTION_CLOSE_IME =
- PACKAGE_NAME + ".action.CLOSE_IME";
- public static final String ACTION_OPEN_IME =
- PACKAGE_NAME + ".action.OPEN_IME";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".ImeActivity");
- }
-
- public static class SplitScreenActivity {
- public static final String LABEL = "SplitScreenPrimaryApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SplitScreenActivity");
- }
-
- public static class SplitScreenSecondaryActivity {
- public static final String LABEL = "SplitScreenSecondaryApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SplitScreenSecondaryActivity");
- }
-
- public static class SendNotificationActivity {
- public static final String LABEL = "SendNotificationApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SendNotificationActivity");
- }
-
- public static class LaunchBubbleActivity {
- public static final String LABEL = "LaunchBubbleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".LaunchBubbleActivity");
- }
-
- public static class BubbleActivity {
- public static final String LABEL = "BubbleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".BubbleActivity");
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java
deleted file mode 100644
index d4ae6c1313bf..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import static com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION;
-
-import android.os.Bundle;
-
-public class FixedActivity extends SimpleActivity {
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Set the fixed orientation if requested
- if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
- final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
- setRequestedOrientation(ori);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
deleted file mode 100644
index 59c64a1345ab..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 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.testapp;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-public class ImeActivity extends Activity {
- private static final String ACTION_OPEN_IME =
- "com.android.wm.shell.flicker.testapp.action.OPEN_IME";
- private static final String ACTION_CLOSE_IME =
- "com.android.wm.shell.flicker.testapp.action.CLOSE_IME";
-
- private InputMethodManager mImm;
- private View mEditText;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
- setContentView(R.layout.activity_ime);
-
- mEditText = findViewById(R.id.plain_text_input);
- mImm = getSystemService(InputMethodManager.class);
-
- handleIntent(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- final String action = intent.getAction();
- if (ACTION_OPEN_IME.equals(action)) {
- mEditText.requestFocus();
- mImm.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED);
- } else if (ACTION_CLOSE_IME.equals(action)) {
- mImm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
- mEditText.clearFocus();
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java
deleted file mode 100644
index 71fa66d8a61c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-
-import android.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.view.View;
-
-import java.util.Arrays;
-
-public class LaunchBubbleActivity extends Activity {
-
- private BubbleHelper mBubbleHelper;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addInboxShortcut(getApplicationContext());
- mBubbleHelper = BubbleHelper.getInstance(this);
- setContentView(R.layout.activity_main);
- findViewById(R.id.button_create).setOnClickListener(this::add);
- findViewById(R.id.button_cancel).setOnClickListener(this::cancel);
- findViewById(R.id.button_cancel_all).setOnClickListener(this::cancelAll);
- }
-
- private void add(View v) {
- mBubbleHelper.addNewBubble(false /* autoExpand */, false /* suppressNotif */);
- }
-
- private void cancel(View v) {
- mBubbleHelper.cancelLast();
- }
-
- private void cancelAll(View v) {
- mBubbleHelper.cancelAll();
- }
-
- private void addInboxShortcut(Context context) {
- Icon icon = Icon.createWithResource(this, R.drawable.bg);
- Person[] persons = new Person[4];
- for (int i = 0; i < persons.length; i++) {
- persons[i] = new Person.Builder()
- .setBot(false)
- .setIcon(icon)
- .setName("google" + i)
- .setImportant(true)
- .build();
- }
-
- ShortcutInfo shortcut = new ShortcutInfo.Builder(context, "BubbleChat")
- .setShortLabel("BubbleChat")
- .setLongLived(true)
- .setIntent(new Intent(Intent.ACTION_VIEW))
- .setIcon(Icon.createWithResource(context, R.drawable.ic_message))
- .setPersons(persons)
- .build();
- ShortcutManager scmanager = context.getSystemService(ShortcutManager.class);
- scmanager.addDynamicShortcuts(Arrays.asList(shortcut));
- }
-
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java
deleted file mode 100644
index 24275e002c7f..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class NonResizeableActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.activity_non_resizeable);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
deleted file mode 100644
index 615b1730579c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import static android.media.MediaMetadata.METADATA_KEY_TITLE;
-import static android.media.session.PlaybackState.ACTION_PAUSE;
-import static android.media.session.PlaybackState.ACTION_PLAY;
-import static android.media.session.PlaybackState.ACTION_STOP;
-import static android.media.session.PlaybackState.STATE_PAUSED;
-import static android.media.session.PlaybackState.STATE_PLAYING;
-import static android.media.session.PlaybackState.STATE_STOPPED;
-
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.drawable.Icon;
-import android.media.MediaMetadata;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.Rational;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.RadioButton;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-public class PipActivity extends Activity {
- private static final String TAG = PipActivity.class.getSimpleName();
- /**
- * A media session title for when the session is in {@link STATE_PLAYING}.
- * TvPipNotificationTests check whether the actual notification title matches this string.
- */
- private static final String TITLE_STATE_PLAYING = "TestApp media is playing";
- /**
- * A media session title for when the session is in {@link STATE_PAUSED}.
- * TvPipNotificationTests check whether the actual notification title matches this string.
- */
- private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
-
- private static final Rational RATIO_DEFAULT = null;
- private static final Rational RATIO_SQUARE = new Rational(1, 1);
- private static final Rational RATIO_WIDE = new Rational(2, 1);
- private static final Rational RATIO_TALL = new Rational(1, 2);
-
- private static final String PIP_ACTION_NO_OP = "No-Op";
- private static final String PIP_ACTION_OFF = "Off";
- private static final String PIP_ACTION_ON = "On";
- private static final String PIP_ACTION_CLEAR = "Clear";
- private static final String ACTION_NO_OP = "com.android.wm.shell.flicker.testapp.NO_OP";
- private static final String ACTION_SWITCH_OFF =
- "com.android.wm.shell.flicker.testapp.SWITCH_OFF";
- private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON";
- private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR";
-
- private final PictureInPictureParams.Builder mPipParamsBuilder =
- new PictureInPictureParams.Builder()
- .setAspectRatio(RATIO_DEFAULT);
- private MediaSession mMediaSession;
- private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
- .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP)
- .setState(STATE_STOPPED, 0, 1f);
- private PlaybackState mPlaybackState = mPlaybackStateBuilder.build();
- private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder();
-
- private final List<RemoteAction> mSwitchOffActions = new ArrayList<>();
- private final List<RemoteAction> mSwitchOnActions = new ArrayList<>();
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (isInPictureInPictureMode()) {
- switch (intent.getAction()) {
- case ACTION_SWITCH_ON:
- mPipParamsBuilder.setActions(mSwitchOnActions);
- break;
- case ACTION_SWITCH_OFF:
- mPipParamsBuilder.setActions(mSwitchOffActions);
- break;
- case ACTION_CLEAR:
- mPipParamsBuilder.setActions(Collections.emptyList());
- break;
- case ACTION_NO_OP:
- return;
- default:
- Log.w(TAG, "Unhandled action=" + intent.getAction());
- return;
- }
- setPictureInPictureParams(mPipParamsBuilder.build());
- } else {
- switch (intent.getAction()) {
- case ACTION_ENTER_PIP:
- enterPip(null);
- break;
- case ACTION_SET_REQUESTED_ORIENTATION:
- setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
- EXTRA_PIP_ORIENTATION)));
- break;
- default:
- Log.w(TAG, "Unhandled action=" + intent.getAction());
- return;
- }
- }
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Window window = getWindow();
- final WindowManager.LayoutParams layoutParams = window.getAttributes();
- layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- window.setAttributes(layoutParams);
-
- setContentView(R.layout.activity_pip);
-
- findViewById(R.id.media_session_start)
- .setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING));
- findViewById(R.id.media_session_stop)
- .setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED));
-
- mMediaSession = new MediaSession(this, "WMShell_TestApp");
- mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
- mMediaSession.setCallback(new MediaSession.Callback() {
- @Override
- public void onPlay() {
- updateMediaSessionState(STATE_PLAYING);
- }
-
- @Override
- public void onPause() {
- updateMediaSessionState(STATE_PAUSED);
- }
-
- @Override
- public void onStop() {
- updateMediaSessionState(STATE_STOPPED);
- }
- });
-
- // Build two sets of the custom actions. We'll replace one with the other when 'On'/'Off'
- // action is invoked.
- // The first set consists of 3 actions: 1) Off; 2) No-Op; 3) Clear.
- // The second set consists of 2 actions: 1) On; 2) Clear.
- // Upon invocation 'Clear' action clear-off all the custom actions, including itself.
- final Icon icon = Icon.createWithResource(this, android.R.drawable.ic_menu_help);
- final RemoteAction noOpAction = buildRemoteAction(icon, PIP_ACTION_NO_OP, ACTION_NO_OP);
- final RemoteAction switchOnAction =
- buildRemoteAction(icon, PIP_ACTION_ON, ACTION_SWITCH_ON);
- final RemoteAction switchOffAction =
- buildRemoteAction(icon, PIP_ACTION_OFF, ACTION_SWITCH_OFF);
- final RemoteAction clearAllAction = buildRemoteAction(icon, PIP_ACTION_CLEAR, ACTION_CLEAR);
- mSwitchOffActions.addAll(Arrays.asList(switchOnAction, clearAllAction));
- mSwitchOnActions.addAll(Arrays.asList(noOpAction, switchOffAction, clearAllAction));
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_NO_OP);
- filter.addAction(ACTION_SWITCH_ON);
- filter.addAction(ACTION_SWITCH_OFF);
- filter.addAction(ACTION_CLEAR);
- filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
- filter.addAction(ACTION_ENTER_PIP);
- registerReceiver(mBroadcastReceiver, filter);
-
- handleIntentExtra(getIntent());
- }
-
- @Override
- protected void onDestroy() {
- unregisterReceiver(mBroadcastReceiver);
- super.onDestroy();
- }
-
- @Override
- protected void onUserLeaveHint() {
- // Only used when auto PiP is disabled. This is to simulate the behavior that an app
- // supports regular PiP but not auto PiP.
- final boolean manuallyEnterPip =
- ((RadioButton) findViewById(R.id.enter_pip_on_leave_manual)).isChecked();
- if (manuallyEnterPip) {
- enterPictureInPictureMode();
- }
- }
-
- private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
- final Intent intent = new Intent(action);
- final PendingIntent pendingIntent =
- PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- return new RemoteAction(icon, label, label, pendingIntent);
- }
-
- public void enterPip(View v) {
- final boolean withCustomActions =
- ((CheckBox) findViewById(R.id.with_custom_actions)).isChecked();
- mPipParamsBuilder.setActions(
- withCustomActions ? mSwitchOnActions : Collections.emptyList());
- enterPictureInPictureMode(mPipParamsBuilder.build());
- }
-
- public void onAutoPipSelected(View v) {
- switch (v.getId()) {
- case R.id.enter_pip_on_leave_manual:
- // disable auto enter PiP
- case R.id.enter_pip_on_leave_disabled:
- mPipParamsBuilder.setAutoEnterEnabled(false);
- setPictureInPictureParams(mPipParamsBuilder.build());
- break;
- case R.id.enter_pip_on_leave_autoenter:
- mPipParamsBuilder.setAutoEnterEnabled(true);
- setPictureInPictureParams(mPipParamsBuilder.build());
- break;
- }
- }
-
- public void onRatioSelected(View v) {
- switch (v.getId()) {
- case R.id.ratio_default:
- mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT);
- break;
-
- case R.id.ratio_square:
- mPipParamsBuilder.setAspectRatio(RATIO_SQUARE);
- break;
-
- case R.id.ratio_wide:
- mPipParamsBuilder.setAspectRatio(RATIO_WIDE);
- break;
-
- case R.id.ratio_tall:
- mPipParamsBuilder.setAspectRatio(RATIO_TALL);
- break;
- }
- }
-
- private void updateMediaSessionState(int newState) {
- if (mPlaybackState.getState() == newState) {
- return;
- }
- final String title;
- switch (newState) {
- case STATE_PLAYING:
- title = TITLE_STATE_PLAYING;
- break;
- case STATE_PAUSED:
- title = TITLE_STATE_PAUSED;
- break;
- case STATE_STOPPED:
- title = "";
- break;
-
- default:
- throw new IllegalArgumentException("Unknown state " + newState);
- }
-
- mPlaybackStateBuilder.setState(newState, 0, 1f);
- mPlaybackState = mPlaybackStateBuilder.build();
-
- mMediaMetadataBuilder.putText(METADATA_KEY_TITLE, title);
-
- mMediaSession.setPlaybackState(mPlaybackState);
- mMediaSession.setMetadata(mMediaMetadataBuilder.build());
- mMediaSession.setActive(newState != STATE_STOPPED);
- }
-
- private void handleIntentExtra(Intent intent) {
- // Set the fixed orientation if requested
- if (intent.hasExtra(EXTRA_PIP_ORIENTATION)) {
- final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
- setRequestedOrientation(ori);
- }
- // Enter picture in picture with the given aspect ratio if provided
- if (intent.hasExtra(EXTRA_ENTER_PIP)) {
- mPipParamsBuilder.setActions(mSwitchOnActions);
- enterPip(null);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
deleted file mode 100644
index 8020ef2270a0..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
+++ /dev/null
@@ -1,61 +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.flicker.testapp;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-
-public class SendNotificationActivity extends Activity {
- private NotificationManager mNotificationManager;
- private String mChannelId = "Channel id";
- private String mChannelName = "Channel name";
- private NotificationChannel mChannel;
- private int mNotifyId = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_notification);
- findViewById(R.id.button_send_notification).setOnClickListener(this::sendNotification);
-
- mChannel = new NotificationChannel(mChannelId, mChannelName,
- NotificationManager.IMPORTANCE_DEFAULT);
- mNotificationManager = getSystemService(NotificationManager.class);
- mNotificationManager.createNotificationChannel(mChannel);
- }
-
- private void sendNotification(View v) {
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
- new Intent(this, SendNotificationActivity.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- Notification notification = new Notification.Builder(this, mChannelId)
- .setContentTitle("Notification App")
- .setContentText("Notification content")
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.ic_message)
- .setContentIntent(pendingIntent)
- .build();
-
- mNotificationManager.notify(mNotifyId, notification);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java
deleted file mode 100644
index 5343c1893d4e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2018 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.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class SimpleActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
- .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
- setContentView(R.layout.activity_simple);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java
deleted file mode 100644
index baa1e6fdd1e9..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SplitScreenSecondaryActivity extends Activity {
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.activity_splitscreen_secondary);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index b5ee037892ba..51a20ee9d090 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -17,8 +17,10 @@
package com.android.wm.shell;
import static android.view.Display.DEFAULT_DISPLAY;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.testing.TestableContext;
@@ -36,6 +38,7 @@ import org.mockito.MockitoAnnotations;
public abstract class ShellTestCase {
protected TestableContext mContext;
+ private PackageManager mPm;
@Before
public void shellSetup() {
@@ -46,6 +49,7 @@ public abstract class ShellTestCase {
final Context context =
InstrumentationRegistry.getInstrumentation().getTargetContext();
final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ mPm = context.getPackageManager();
mContext = new TestableContext(
context.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY)));
@@ -66,4 +70,20 @@ public abstract class ShellTestCase {
protected Context getContext() {
return mContext;
}
+
+ /**
+ * Makes an assumption that the test device is a TV device, used to guard tests that should
+ * only be run on TVs.
+ */
+ protected void assumeTelevision() {
+ assumeTrue(isTelevision());
+ }
+
+ /**
+ * Returns whether this test device is a TV device.
+ */
+ protected boolean isTelevision() {
+ return mPm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || mPm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index fe8b305093d7..da95c77d2b89 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -48,10 +48,9 @@ public class TestShellExecutor implements ShellExecutor {
}
public void flushAll() {
- final ArrayList<Runnable> tmpRunnable = new ArrayList<>(mRunnables);
- mRunnables.clear();
- for (Runnable r : tmpRunnable) {
+ for (Runnable r : mRunnables) {
r.run();
}
+ mRunnables.clear();
}
}
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 2e328b0736dd..8a5b4901ed96 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
@@ -18,13 +18,12 @@ package com.android.wm.shell.back;
import static android.window.BackNavigationInfo.KEY_TRIGGER_BACK;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -39,7 +38,7 @@ import android.app.WindowConfiguration;
import android.content.pm.ApplicationInfo;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
@@ -49,14 +48,17 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableContentResolver;
import android.testing.TestableContext;
import android.testing.TestableLooper;
+import android.view.IRemoteAnimationRunner;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.BackEvent;
+import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
-import android.window.IBackNaviAnimationController;
+import android.window.IBackAnimationFinishedCallback;
import android.window.IOnBackInvokedCallback;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -68,7 +70,6 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -93,23 +94,27 @@ public class BackAnimationControllerTest extends ShellTestCase {
new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
@Mock
- private SurfaceControl.Transaction mTransaction;
+ private IActivityTaskManager mActivityTaskManager;
@Mock
- private IActivityTaskManager mActivityTaskManager;
+ private IOnBackInvokedCallback mAppCallback;
@Mock
- private IOnBackInvokedCallback mIOnBackInvokedCallback;
+ private IOnBackInvokedCallback mAnimatorCallback;
@Mock
- private IBackNaviAnimationController mIBackNaviAnimationController;
+ private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
+
+ @Mock
+ private IRemoteAnimationRunner mBackAnimationRunner;
@Mock
private ShellController mShellController;
- private BackAnimationController mController;
+ @Mock
+ private BackAnimationBackground mAnimationBackground;
- private int mEventTime = 0;
+ private BackAnimationController mController;
private TestableContentResolver mContentResolver;
private TestableLooper mTestableLooper;
@@ -124,29 +129,20 @@ public class BackAnimationControllerTest extends ShellTestCase {
mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
mController = new BackAnimationController(mShellInit, mShellController,
- mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
+ mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver);
+ mContentResolver, mAnimationBackground);
mController.setEnableUAnimation(true);
mShellInit.init();
- mEventTime = 0;
mShellExecutor.flushAll();
}
- private void createNavigationInfo(RemoteAnimationTarget topAnimationTarget,
- SurfaceControl screenshotSurface,
- HardwareBuffer hardwareBuffer,
- int backType,
- IOnBackInvokedCallback onBackInvokedCallback, boolean prepareAnimation) {
+ private void createNavigationInfo(int backType, boolean enableAnimation) {
BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
.setType(backType)
- .setDepartingAnimationTarget(topAnimationTarget)
- .setScreenshotSurface(screenshotSurface)
- .setScreenshotBuffer(hardwareBuffer)
- .setTaskWindowConfiguration(new WindowConfiguration())
.setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
- .setOnBackInvokedCallback(onBackInvokedCallback)
- .setPrepareAnimation(prepareAnimation);
+ .setOnBackInvokedCallback(mAppCallback)
+ .setPrepareRemoteAnimation(enableAnimation);
createNavigationInfo(builder);
}
@@ -154,7 +150,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
private void createNavigationInfo(BackNavigationInfo.Builder builder) {
try {
doReturn(builder.build()).when(mActivityTaskManager)
- .startBackNavigation(anyBoolean(), any(), any());
+ .startBackNavigation(any(), any());
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
@@ -187,74 +183,57 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
- @Ignore("b/207481538")
- public void crossActivity_screenshotAttachedAndVisible() {
- SurfaceControl screenshotSurface = new SurfaceControl();
- HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
- createNavigationInfo(createAnimationTarget(), screenshotSurface, hardwareBuffer,
- BackNavigationInfo.TYPE_CROSS_ACTIVITY, null, true);
- doMotionEvent(MotionEvent.ACTION_DOWN, 0);
- verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
- verify(mTransaction).setVisibility(screenshotSurface, true);
- verify(mTransaction).apply();
- }
+ 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) {
+ registerAnimation(type);
+ }
- @Test
- public void crossActivity_surfaceMovesWithGesture() {
- SurfaceControl screenshotSurface = new SurfaceControl();
- HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- createNavigationInfo(animationTarget, screenshotSurface, hardwareBuffer,
- BackNavigationInfo.TYPE_CROSS_ACTIVITY, null, true);
- doMotionEvent(MotionEvent.ACTION_DOWN, 0);
- doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- // b/207481538, we check that the surface is not moved for now, we can re-enable this once
- // we implement the animation
- verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt());
- verify(mTransaction, never()).setPosition(
- animationTarget.leash, 100, 100);
- verify(mTransaction, atLeastOnce()).apply();
- }
+ for (int type: testTypes) {
+ final ResultListener result = new ResultListener();
+ createNavigationInfo(new BackNavigationInfo.Builder()
+ .setType(type)
+ .setOnBackInvokedCallback(mAppCallback)
+ .setPrepareRemoteAnimation(true)
+ .setOnBackNavigationDone(new RemoteCallback(result)));
+ triggerBackGesture();
+ simulateRemoteAnimationStart(type);
+ simulateRemoteAnimationFinished();
+ mShellExecutor.flushAll();
- @Test
- public void verifyAnimationFinishes() {
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- boolean[] backNavigationDone = new boolean[]{false};
- boolean[] triggerBack = new boolean[]{false};
- createNavigationInfo(new BackNavigationInfo.Builder()
- .setDepartingAnimationTarget(animationTarget)
- .setType(BackNavigationInfo.TYPE_CROSS_ACTIVITY)
- .setOnBackNavigationDone(
- new RemoteCallback(result -> {
- backNavigationDone[0] = true;
- triggerBack[0] = result.getBoolean(KEY_TRIGGER_BACK);
- })));
- triggerBackGesture();
- assertTrue("Navigation Done callback not called", backNavigationDone[0]);
- assertTrue("TriggerBack should have been true", triggerBack[0]);
+ assertTrue("Navigation Done callback not called for "
+ + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+ assertTrue("TriggerBack should have been true", result.mTriggerBack);
+ }
}
@Test
public void backToHome_dispatchesEvents() throws RemoteException {
- mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- createNavigationInfo(animationTarget, null, null,
- BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true);
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
- verify(mIOnBackInvokedCallback).onBackStarted(backEventCaptor.capture());
- assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
- verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(any(BackEvent.class));
+
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+
+ verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class));
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+ ArgumentCaptor<BackMotionEvent> backEventCaptor =
+ ArgumentCaptor.forClass(BackMotionEvent.class);
+ verify(mAnimatorCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture());
// Check that back invocation is dispatched.
mController.setTriggerBack(true); // Fake trigger back
doMotionEvent(MotionEvent.ACTION_UP, 0);
- verify(mIOnBackInvokedCallback).onBackInvoked();
+ verify(mAnimatorCallback).onBackInvoked();
}
@Test
@@ -263,93 +242,100 @@ public class BackAnimationControllerTest extends ShellTestCase {
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()), mTransaction,
+ mShellExecutor, new Handler(mTestableLooper.getLooper()),
mActivityTaskManager, mContext,
- mContentResolver);
+ mContentResolver, mAnimationBackground);
shellInit.init();
- mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class);
- ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
- createNavigationInfo(animationTarget, null, null,
- BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback, false);
+ ArgumentCaptor<BackMotionEvent> backEventCaptor =
+ ArgumentCaptor.forClass(BackMotionEvent.class);
+
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false);
triggerBackGesture();
- verify(appCallback, never()).onBackStarted(any(BackEvent.class));
- verify(appCallback, never()).onBackProgressed(backEventCaptor.capture());
- verify(appCallback, times(1)).onBackInvoked();
+ verify(mAppCallback, never()).onBackStarted(any());
+ verify(mAppCallback, never()).onBackProgressed(backEventCaptor.capture());
+ verify(mAppCallback, times(1)).onBackInvoked();
- verify(mIOnBackInvokedCallback, never()).onBackStarted(any(BackEvent.class));
- verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
- verify(mIOnBackInvokedCallback, never()).onBackInvoked();
+ verify(mAnimatorCallback, never()).onBackStarted(any());
+ verify(mAnimatorCallback, never()).onBackProgressed(backEventCaptor.capture());
+ verify(mAnimatorCallback, never()).onBackInvoked();
+ verify(mBackAnimationRunner, never()).onAnimationStart(
+ anyInt(), any(), any(), any(), any());
}
@Test
public void ignoresGesture_transitionInProgress() throws RemoteException {
- mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- createNavigationInfo(animationTarget, null, null,
- BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true);
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
triggerBackGesture();
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
// Check that back invocation is dispatched.
- verify(mIOnBackInvokedCallback).onBackInvoked();
+ verify(mAnimatorCallback).onBackInvoked();
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
+
+ reset(mAnimatorCallback);
+ reset(mBackAnimationRunner);
- reset(mIOnBackInvokedCallback);
// Verify that we prevent animation from restarting if another gestures happens before
// the previous transition is finished.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
- verifyNoMoreInteractions(mIOnBackInvokedCallback);
- mController.onBackToLauncherAnimationFinished();
+ verifyNoMoreInteractions(mAnimatorCallback);
+
+ // Finish back navigation.
+ simulateRemoteAnimationFinished();
// Verify that more events from a rejected swipe cannot start animation.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
doMotionEvent(MotionEvent.ACTION_UP, 0);
- verifyNoMoreInteractions(mIOnBackInvokedCallback);
+ verifyNoMoreInteractions(mAnimatorCallback);
// Verify that we start accepting gestures again once transition finishes.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
+
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ verify(mAnimatorCallback).onBackStarted(any());
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
}
@Test
public void acceptsGesture_transitionTimeout() throws RemoteException {
- mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
- createNavigationInfo(animationTarget, null, null,
- BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true);
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
+
+ // In case it is still running in animation.
+ doNothing().when(mAnimatorCallback).onBackInvoked();
triggerBackGesture();
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- reset(mIOnBackInvokedCallback);
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
// Simulate transition timeout.
mShellExecutor.flushAll();
+ reset(mAnimatorCallback);
+
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ verify(mAnimatorCallback).onBackStarted(any());
}
-
@Test
public void cancelBackInvokeWhenLostFocus() throws RemoteException {
- mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
- RemoteAnimationTarget animationTarget = createAnimationTarget();
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
- createNavigationInfo(animationTarget, null, null,
- BackNavigationInfo.TYPE_RETURN_TO_HOME, null, true);
+ createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
- simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
+
+ simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ verify(mAnimatorCallback).onBackStarted(any());
+ verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
// Check that back invocation is dispatched.
mController.setTriggerBack(true); // Fake trigger back
@@ -358,11 +344,74 @@ public class BackAnimationControllerTest extends ShellTestCase {
IBinder token = mock(IBinder.class);
mController.mFocusObserver.focusLost(token);
mShellExecutor.flushAll();
- verify(mIOnBackInvokedCallback).onBackCancelled();
+ verify(mAnimatorCallback).onBackCancelled();
// No more back invoke.
doMotionEvent(MotionEvent.ACTION_UP, 0);
- verify(mIOnBackInvokedCallback, never()).onBackInvoked();
+ verify(mAnimatorCallback, never()).onBackInvoked();
+ }
+
+ @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) {
+ unregisterAnimation(type);
+ }
+
+ for (int type: testTypes) {
+ final ResultListener result = new ResultListener();
+ createNavigationInfo(new BackNavigationInfo.Builder()
+ .setType(type)
+ .setOnBackInvokedCallback(mAppCallback)
+ .setPrepareRemoteAnimation(true)
+ .setOnBackNavigationDone(new RemoteCallback(result)));
+ triggerBackGesture();
+ simulateRemoteAnimationStart(type);
+ mShellExecutor.flushAll();
+
+ assertTrue("Navigation Done callback not called for "
+ + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+ assertTrue("TriggerBack should have been true", result.mTriggerBack);
+ }
+
+ verify(mAppCallback, never()).onBackStarted(any());
+ verify(mAppCallback, never()).onBackProgressed(any());
+ verify(mAppCallback, times(testTypes.length)).onBackInvoked();
+
+ verify(mAnimatorCallback, never()).onBackStarted(any());
+ verify(mAnimatorCallback, never()).onBackProgressed(any());
+ verify(mAnimatorCallback, never()).onBackInvoked();
+ }
+
+ @Test
+ public void callbackShouldDeliverProgress() throws RemoteException {
+ registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+
+ final int type = BackNavigationInfo.TYPE_CALLBACK;
+ final ResultListener result = new ResultListener();
+ createNavigationInfo(new BackNavigationInfo.Builder()
+ .setType(type)
+ .setOnBackInvokedCallback(mAppCallback)
+ .setOnBackNavigationDone(new RemoteCallback(result)));
+ triggerBackGesture();
+ mShellExecutor.flushAll();
+
+ assertTrue("Navigation Done callback not called for "
+ + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+ assertTrue("TriggerBack should have been true", result.mTriggerBack);
+
+ verify(mAppCallback, times(1)).onBackStarted(any());
+ verify(mAppCallback, times(1)).onBackProgressed(any());
+ verify(mAppCallback, times(1)).onBackInvoked();
+
+ verify(mAnimatorCallback, never()).onBackStarted(any());
+ verify(mAnimatorCallback, never()).onBackProgressed(any());
+ verify(mAnimatorCallback, never()).onBackInvoked();
}
private void doMotionEvent(int actionDown, int coordinate) {
@@ -370,16 +419,39 @@ public class BackAnimationControllerTest extends ShellTestCase {
coordinate, coordinate,
actionDown,
BackEvent.EDGE_LEFT);
- mEventTime += 10;
}
- private void simulateRemoteAnimationStart(int type, RemoteAnimationTarget animationTarget)
- throws RemoteException {
- if (mController.mIBackAnimationRunner != null) {
- final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
- mController.mIBackAnimationRunner.onAnimationStart(mIBackNaviAnimationController, type,
- targets, null, null);
+ private void simulateRemoteAnimationStart(int type) throws RemoteException {
+ RemoteAnimationTarget animationTarget = createAnimationTarget();
+ RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
+ if (mController.mBackAnimationAdapter != null) {
+ mController.mBackAnimationAdapter.getRunner().onAnimationStart(type,
+ targets, null, null, mBackAnimationFinishedCallback);
mShellExecutor.flushAll();
}
}
+
+ private void simulateRemoteAnimationFinished() {
+ mController.onBackAnimationFinished();
+ mController.finishBackNavigation();
+ }
+
+ private void registerAnimation(int type) {
+ mController.registerAnimation(type,
+ new BackAnimationRunner(mAnimatorCallback, mBackAnimationRunner));
+ }
+
+ private void unregisterAnimation(int type) {
+ mController.unregisterAnimation(type);
+ }
+
+ 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/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
new file mode 100644
index 000000000000..3608474bd90e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.back;
+
+import static android.window.BackEvent.EDGE_LEFT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.window.BackEvent;
+import android.window.BackMotionEvent;
+import android.window.BackProgressAnimator;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class BackProgressAnimatorTest {
+ private BackProgressAnimator mProgressAnimator;
+ private BackEvent mReceivedBackEvent;
+ private float mTargetProgress = 0.5f;
+ private CountDownLatch mTargetProgressCalled = new CountDownLatch(1);
+ private Handler mMainThreadHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ mMainThreadHandler = new Handler(Looper.getMainLooper());
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 0, 0,
+ 0, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> {
+ mProgressAnimator = new BackProgressAnimator();
+ mProgressAnimator.onBackStarted(backEvent, this::onGestureProgress);
+ });
+ }
+
+ @Test
+ public void testBackProgressed() throws InterruptedException {
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 100, 0,
+ mTargetProgress, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackProgressed(backEvent));
+
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+
+ assertNotNull(mReceivedBackEvent);
+ assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+ }
+
+ @Test
+ public void testBackCancelled() throws InterruptedException {
+ // Give the animator some progress.
+ final BackMotionEvent backEvent = new BackMotionEvent(
+ 100, 0,
+ mTargetProgress, EDGE_LEFT, null);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackProgressed(backEvent));
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+ assertNotNull(mReceivedBackEvent);
+
+ // Trigger animation cancel, the target progress should be 0.
+ mTargetProgress = 0;
+ mTargetProgressCalled = new CountDownLatch(1);
+ CountDownLatch cancelCallbackCalled = new CountDownLatch(1);
+ mMainThreadHandler.post(
+ () -> mProgressAnimator.onBackCancelled(() -> cancelCallbackCalled.countDown()));
+ cancelCallbackCalled.await(1, TimeUnit.SECONDS);
+ mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+ assertNotNull(mReceivedBackEvent);
+ assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+ }
+
+ private void onGestureProgress(BackEvent backEvent) {
+ if (mTargetProgress == backEvent.getProgress()) {
+ mReceivedBackEvent = backEvent;
+ mTargetProgressCalled.countDown();
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
index 3aefc3f03a8a..ba9c159bad28 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.back;
import static org.junit.Assert.assertEquals;
import android.window.BackEvent;
+import android.window.BackMotionEvent;
import org.junit.Before;
import org.junit.Test;
@@ -38,7 +39,7 @@ public class TouchTrackerTest {
@Test
public void generatesProgress_onStart() {
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
- BackEvent event = mTouchTracker.createStartEvent(null);
+ BackMotionEvent event = mTouchTracker.createStartEvent(null);
assertEquals(event.getProgress(), 0f, 0f);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 262ee72d86fc..ffb1a4d66f1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -41,7 +41,6 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
-import com.android.internal.view.IInputMethodManager;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.sysui.ShellInit;
@@ -58,8 +57,6 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Mock
private SurfaceControl.Transaction mT;
@Mock
- private IInputMethodManager mMock;
- @Mock
private ShellInit mShellInit;
private DisplayImeController.PerDisplay mPerDisplay;
private Executor mExecutor;
@@ -79,10 +76,6 @@ public class DisplayImeControllerTest extends ShellTestCase {
}
}, mExecutor) {
@Override
- public IInputMethodManager getImms() {
- return mMock;
- }
- @Override
void removeImeSurface() { }
}.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
}
@@ -106,13 +99,13 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.showInsets(ime(), true);
+ mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */);
verifyZeroInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
- mPerDisplay.hideInsets(ime(), true);
+ mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */);
verifyZeroInteractions(mExecutor);
}
@@ -145,13 +138,14 @@ public class DisplayImeControllerTest extends ShellTestCase {
private InsetsSourceControl[] insetsSourceControl() {
return new InsetsSourceControl[]{
new InsetsSourceControl(
- ITYPE_IME, mock(SurfaceControl.class), false, new Point(0, 0), Insets.NONE)
+ ITYPE_IME, ime(), mock(SurfaceControl.class), false, new Point(0, 0),
+ Insets.NONE)
};
}
private InsetsState insetsStateWithIme(boolean visible) {
InsetsState state = new InsetsState();
- state.addSource(new InsetsSource(ITYPE_IME));
+ state.addSource(new InsetsSource(ITYPE_IME, ime()));
state.setSourceVisible(ITYPE_IME, visible);
return state;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
index 5f5a3c584ee0..956f1cd419c2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
import android.util.SparseArray;
@@ -32,7 +33,8 @@ import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsVisibilities;
+import android.view.WindowInsets;
+import android.view.inputmethod.ImeTracker;
import androidx.test.filters.SmallTest;
@@ -108,11 +110,13 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
mController.addInsetsChangedListener(SECOND_DISPLAY, secondListener);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).topFocusedWindowChanged(null,
- new InsetsVisibilities());
+ WindowInsets.Type.defaultVisible());
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
- mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false);
- mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false,
+ null /* statsToken */);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false,
+ null /* statsToken */);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -128,11 +132,13 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
assertTrue(secondListener.hideInsetsCount == 0);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).topFocusedWindowChanged(null,
- new InsetsVisibilities());
+ WindowInsets.Type.defaultVisible());
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
- mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false);
- mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false,
+ null /* statsToken */);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false,
+ null /* statsToken */);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
@@ -175,8 +181,7 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
int hideInsetsCount = 0;
@Override
- public void topFocusedWindowChanged(ComponentName component,
- InsetsVisibilities requestedVisibilities) {
+ public void topFocusedWindowChanged(ComponentName component, int requestedVisibleTypes) {
topFocusedWindowChangedCount++;
}
@@ -192,12 +197,12 @@ public class DisplayInsetsControllerTest extends ShellTestCase {
}
@Override
- public void showInsets(int types, boolean fromIme) {
+ public void showInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {
showInsetsCount++;
}
@Override
- public void hideInsets(int types, boolean fromIme) {
+ public void hideInsets(int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {
hideInsetsCount++;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 2fc0914acbd4..d4408d78eba1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.compatui;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -291,7 +292,7 @@ public class CompatUIControllerTest extends ShellTestCase {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
InsetsState insetsState = new InsetsState();
- InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR, navigationBars());
insetsSource.setFrame(0, 0, 1000, 1000);
insetsState.addSource(insetsSource);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index e79b803b4304..c4d78bbb2004 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -21,6 +21,7 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -328,7 +329,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
// Update if the insets change on the existing display layout
clearInvocations(mWindowManager);
InsetsState insetsState = new InsetsState();
- InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR, navigationBars());
insetsSource.setFrame(0, 0, 1000, 1000);
insetsState.addSource(insetsSource);
displayLayout.setInsets(mContext.getResources(), insetsState);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
new file mode 100644
index 000000000000..736d4cff6ce8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
@@ -0,0 +1,3 @@
+# WM shell sub-module TV pip owners
+galinap@google.com
+bronger@google.com \ No newline at end of file
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
new file mode 100644
index 000000000000..e5b61ed2fd25
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
@@ -0,0 +1,328 @@
+/*
+ * 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.pip.tv;
+
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CLOSE;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM;
+import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_CUSTOM_CLOSE;
+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 org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+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.pip.PipMediaController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link TvPipActionsProvider}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class TvPipActionProviderTest extends ShellTestCase {
+ private static final String TAG = TvPipActionProviderTest.class.getSimpleName();
+ private TvPipActionsProvider mActionsProvider;
+
+ @Mock
+ private PipMediaController mMockPipMediaController;
+ @Mock
+ private TvPipActionsProvider.Listener mMockListener;
+ @Mock
+ private TvPipAction.SystemActionsHandler mMockSystemActionsHandler;
+ @Mock
+ private Icon mMockIcon;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private RemoteAction createRemoteAction(int identifier) {
+ 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));
+ }
+ 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;
+ }
+
+ @Before
+ public void setUp() {
+ if (!isTelevision()) {
+ return;
+ }
+ MockitoAnnotations.initMocks(this);
+ mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController,
+ mMockSystemActionsHandler);
+ }
+
+ @Test
+ public void defaultSystemActions_regularPip() {
+ assumeTelevision();
+ mActionsProvider.updateExpansionEnabled(false);
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ }
+
+ @Test
+ public void defaultSystemActions_expandedPip() {
+ assumeTelevision();
+ mActionsProvider.updateExpansionEnabled(true);
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ }
+
+ @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}));
+ 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}));
+ 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}));
+ }
+
+ @Test
+ public void expandedPip_toggleExpansion() {
+ assumeTelevision();
+ // PiP has expanded PiP enabled, but is in a collapsed state
+ mActionsProvider.updateExpansionEnabled(true);
+ mActionsProvider.onPipExpansionToggled(/* expanded= */ false);
+
+ mActionsProvider.addListener(mMockListener);
+ mActionsProvider.onPipExpansionToggled(/* expanded= */ true);
+
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE}));
+ verify(mMockListener).onActionsChanged(0, 1, 3);
+ }
+
+ @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}));
+ 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}));
+ 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);
+
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE}));
+ 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));
+
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE}));
+ 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);
+ 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}));
+ 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}));
+ 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);
+
+ mActionsProvider.addListener(mMockListener);
+ mActionsProvider.setAppActions(createRemoteActions(0), null);
+
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE}));
+ 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));
+
+ assertTrue(checkActionsMatch(mActionsProvider.getActionsList(),
+ new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM,
+ ACTION_MOVE}));
+ 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}));
+ }
+
+ @Test
+ public void mediaActions_hideDisabledActions() {
+ assumeTelevision();
+ mActionsProvider.updateExpansionEnabled(false);
+
+ 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}));
+ }
+
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e472245b4a..7370ed71bbdd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -24,6 +24,7 @@ import android.os.test.TestLooper
import android.testing.AndroidTestingRunner
import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
import com.android.wm.shell.pip.tv.TvPipBoundsController.POSITION_DEBOUNCE_TIMEOUT_MILLIS
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
@@ -43,7 +44,7 @@ import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
-class TvPipBoundsControllerTest {
+class TvPipBoundsControllerTest : ShellTestCase() {
val ANIMATION_DURATION = 100
val STASH_DURATION = 5000
val FAR_FUTURE = 60 * 60000L
@@ -71,7 +72,7 @@ class TvPipBoundsControllerTest {
var inMoveMode = false
@Mock
- lateinit var context: Context
+ lateinit var mockContext: Context
@Mock
lateinit var resources: Resources
@Mock
@@ -83,6 +84,9 @@ class TvPipBoundsControllerTest {
@Before
fun setUp() {
+ if (!isTelevision) {
+ return
+ }
MockitoAnnotations.initMocks(this)
time = 0L
inMenu = false
@@ -91,13 +95,13 @@ class TvPipBoundsControllerTest {
testLooper = TestLooper { time }
mainHandler = Handler(testLooper.getLooper())
- whenever(context.resources).thenReturn(resources)
+ whenever(mockContext.resources).thenReturn(resources)
whenever(resources.getInteger(R.integer.config_pipStashDuration)).thenReturn(STASH_DURATION)
whenever(tvPipBoundsAlgorithm.adjustBoundsForTemporaryDecor(any()))
.then(returnsFirstArg<Rect>())
boundsController = TvPipBoundsController(
- context,
+ mockContext,
{ time },
mainHandler,
tvPipBoundsState,
@@ -107,6 +111,7 @@ class TvPipBoundsControllerTest {
@Test
fun testPlacement_MovedAfterDebounceTimeout() {
+ assumeTelevision()
triggerPlacement(MOVED_PLACEMENT)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS, MOVED_BOUNDS)
assertNoMovementUpTo(time + FAR_FUTURE)
@@ -114,6 +119,7 @@ class TvPipBoundsControllerTest {
@Test
fun testStashedPlacement_MovedAfterDebounceTimeout_Unstashes() {
+ assumeTelevision()
triggerPlacement(STASHED_PLACEMENT_RESTASH)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS, STASHED_BOUNDS)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS + STASH_DURATION, ANCHOR_BOUNDS)
@@ -121,6 +127,7 @@ class TvPipBoundsControllerTest {
@Test
fun testDebounceSamePlacement_MovesDebounceTimeoutAfterFirstPlacement() {
+ assumeTelevision()
triggerPlacement(MOVED_PLACEMENT)
advanceTimeTo(POSITION_DEBOUNCE_TIMEOUT_MILLIS / 2)
triggerPlacement(MOVED_PLACEMENT)
@@ -130,6 +137,7 @@ class TvPipBoundsControllerTest {
@Test
fun testNoMovementUntilPlacementStabilizes() {
+ assumeTelevision()
triggerPlacement(ANCHOR_PLACEMENT)
advanceTimeTo(time + POSITION_DEBOUNCE_TIMEOUT_MILLIS / 10)
triggerPlacement(MOVED_PLACEMENT)
@@ -143,6 +151,7 @@ class TvPipBoundsControllerTest {
@Test
fun testUnstashIfStashNoLongerNecessary() {
+ assumeTelevision()
triggerPlacement(STASHED_PLACEMENT_RESTASH)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS, STASHED_BOUNDS)
@@ -152,6 +161,7 @@ class TvPipBoundsControllerTest {
@Test
fun testRestashingPlacementDelaysUnstash() {
+ assumeTelevision()
triggerPlacement(STASHED_PLACEMENT_RESTASH)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS, STASHED_BOUNDS)
@@ -163,6 +173,7 @@ class TvPipBoundsControllerTest {
@Test
fun testNonRestashingPlacementDoesNotDelayUnstash() {
+ assumeTelevision()
triggerPlacement(STASHED_PLACEMENT_RESTASH)
assertMovementAt(POSITION_DEBOUNCE_TIMEOUT_MILLIS, STASHED_BOUNDS)
@@ -173,13 +184,26 @@ class TvPipBoundsControllerTest {
@Test
fun testImmediatePlacement() {
+ assumeTelevision()
triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
assertMovement(STASHED_BOUNDS)
assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
}
@Test
+ fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+ assumeTelevision()
+ triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+ assertMovement(STASHED_BOUNDS)
+ assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+ triggerImmediatePlacement(STASHED_PLACEMENT)
+ assertNoMovementUpTo(time + FAR_FUTURE)
+ }
+
+ @Test
fun testInMoveMode_KeepAtAnchor() {
+ assumeTelevision()
startMoveMode()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
assertMovement(ANCHOR_BOUNDS)
@@ -188,6 +212,7 @@ class TvPipBoundsControllerTest {
@Test
fun testInMenu_Unstashed() {
+ assumeTelevision()
openPipMenu()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
assertMovement(MOVED_BOUNDS)
@@ -196,6 +221,7 @@ class TvPipBoundsControllerTest {
@Test
fun testCloseMenu_DoNotRestash() {
+ assumeTelevision()
openPipMenu()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
assertMovement(MOVED_BOUNDS)
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
new file mode 100644
index 000000000000..51f86b845a9f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -0,0 +1,341 @@
+/*
+ * 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.pip.tv;
+
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
+import static android.view.KeyEvent.KEYCODE_DPAD_UP;
+
+import static org.junit.Assert.assertEquals;
+
+import android.view.Gravity;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Locale;
+
+public class TvPipGravityTest extends ShellTestCase {
+
+ private static final float VERTICAL_EXPANDED_ASPECT_RATIO = 1f / 3;
+ private static final float HORIZONTAL_EXPANDED_ASPECT_RATIO = 3f;
+
+ @Mock
+ private PipSnapAlgorithm mMockPipSnapAlgorithm;
+
+ private TvPipBoundsState mTvPipBoundsState;
+ private TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
+
+ @Before
+ public void setUp() {
+ if (!isTelevision()) {
+ return;
+ }
+ MockitoAnnotations.initMocks(this);
+ mTvPipBoundsState = new TvPipBoundsState(mContext);
+ mTvPipBoundsAlgorithm = new TvPipBoundsAlgorithm(mContext, mTvPipBoundsState,
+ mMockPipSnapAlgorithm);
+
+ setRTL(false);
+ }
+
+ private void checkGravity(int gravityActual, int gravityExpected) {
+ assertEquals(gravityExpected, gravityActual);
+ }
+
+ private void setRTL(boolean isRtl) {
+ mContext.getResources().getConfiguration().setLayoutDirection(
+ isRtl ? new Locale("ar") : Locale.ENGLISH);
+ mTvPipBoundsState.onConfigurationChanged();
+ mTvPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ }
+
+ private void assertGravityAfterExpansion(int gravityFrom, int gravityTo) {
+ mTvPipBoundsState.setTvPipExpanded(false);
+ mTvPipBoundsState.setTvPipGravity(gravityFrom);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(true);
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), gravityTo);
+ }
+
+ private void assertGravityAfterCollapse(int gravityFrom, int gravityTo) {
+ mTvPipBoundsState.setTvPipExpanded(true);
+ mTvPipBoundsState.setTvPipGravity(gravityFrom);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(false);
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), gravityTo);
+ }
+
+ private void assertGravityAfterExpandAndCollapse(int gravityStartAndEnd) {
+ mTvPipBoundsState.setTvPipGravity(gravityStartAndEnd);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(true);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(false);
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), gravityStartAndEnd);
+ }
+
+ @Test
+ public void regularPip_defaultGravity() {
+ assumeTelevision();
+ checkGravity(mTvPipBoundsState.getDefaultGravity(), 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);
+
+ assertGravityAfterExpansion(Gravity.BOTTOM | Gravity.RIGHT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ assertGravityAfterExpansion(Gravity.TOP | Gravity.RIGHT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ assertGravityAfterExpansion(Gravity.BOTTOM | Gravity.LEFT,
+ Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ assertGravityAfterExpansion(Gravity.TOP | Gravity.LEFT,
+ Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ }
+
+ @Test
+ public void updateGravity_expand_horizontal() {
+ assumeTelevision();
+ // Horizontal expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+
+ assertGravityAfterExpansion(Gravity.BOTTOM | Gravity.RIGHT,
+ Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ assertGravityAfterExpansion(Gravity.TOP | Gravity.RIGHT,
+ Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ assertGravityAfterExpansion(Gravity.BOTTOM | Gravity.LEFT,
+ Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ assertGravityAfterExpansion(Gravity.TOP | Gravity.LEFT,
+ Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ }
+
+ @Test
+ public void updateGravity_collapse() {
+ assumeTelevision();
+ // Vertical expansion
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ assertGravityAfterCollapse(Gravity.CENTER_VERTICAL | Gravity.RIGHT,
+ Gravity.BOTTOM | Gravity.RIGHT);
+ assertGravityAfterCollapse(Gravity.CENTER_VERTICAL | Gravity.LEFT,
+ Gravity.BOTTOM | Gravity.LEFT);
+
+ // Horizontal expansion
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ assertGravityAfterCollapse(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ Gravity.TOP | Gravity.RIGHT);
+ assertGravityAfterCollapse(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
+ Gravity.BOTTOM | Gravity.RIGHT);
+ }
+
+ @Test
+ public void updateGravity_collapse_RTL() {
+ assumeTelevision();
+ setRTL(true);
+
+ // Horizontal expansion
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ assertGravityAfterCollapse(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ Gravity.TOP | Gravity.LEFT);
+ assertGravityAfterCollapse(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
+ Gravity.BOTTOM | Gravity.LEFT);
+ }
+
+ @Test
+ public void updateGravity_expand_collapse() {
+ assumeTelevision();
+ // Vertical expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+
+ assertGravityAfterExpandAndCollapse(Gravity.BOTTOM | Gravity.RIGHT);
+ assertGravityAfterExpandAndCollapse(Gravity.BOTTOM | Gravity.LEFT);
+ assertGravityAfterExpandAndCollapse(Gravity.TOP | Gravity.LEFT);
+ assertGravityAfterExpandAndCollapse(Gravity.TOP | Gravity.RIGHT);
+
+ // Horizontal expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+
+ assertGravityAfterExpandAndCollapse(Gravity.BOTTOM | Gravity.RIGHT);
+ assertGravityAfterExpandAndCollapse(Gravity.BOTTOM | Gravity.LEFT);
+ assertGravityAfterExpandAndCollapse(Gravity.TOP | Gravity.LEFT);
+ assertGravityAfterExpandAndCollapse(Gravity.TOP | Gravity.RIGHT);
+ }
+
+ @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,
+ Gravity.TOP | Gravity.LEFT);
+
+ // Horizontal expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ expandMoveCollapseCheck(Gravity.BOTTOM | Gravity.LEFT, KEYCODE_DPAD_UP,
+ Gravity.TOP | Gravity.LEFT);
+ }
+
+ private void expandMoveCollapseCheck(int gravityFrom, int keycode, int gravityTo) {
+ // Expand
+ mTvPipBoundsState.setTvPipExpanded(false);
+ mTvPipBoundsState.setTvPipGravity(gravityFrom);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(true);
+ // Move
+ mTvPipBoundsAlgorithm.updateGravity(keycode);
+ // Collapse
+ mTvPipBoundsState.setTvPipExpanded(true);
+ mTvPipBoundsAlgorithm.updateGravityOnExpansionToggled(false);
+
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), gravityTo);
+ }
+
+ private void moveAndCheckGravity(int keycode, int gravityEnd, boolean expectChange) {
+ assertEquals(expectChange, mTvPipBoundsAlgorithm.updateGravity(keycode));
+ checkGravity(mTvPipBoundsState.getTvPipGravity(), gravityEnd);
+ }
+
+ @Test
+ public void updateGravity_move_regular_valid() {
+ assumeTelevision();
+ mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.RIGHT);
+ // clockwise
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.LEFT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.LEFT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.TOP | Gravity.RIGHT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.RIGHT, true);
+ // anti-clockwise
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.RIGHT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.TOP | Gravity.LEFT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.LEFT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.BOTTOM | Gravity.RIGHT, true);
+ }
+
+ @Test
+ public void updateGravity_move_expanded_valid() {
+ assumeTelevision();
+ mTvPipBoundsState.setTvPipExpanded(true);
+
+ // Vertical expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.CENTER_VERTICAL | Gravity.LEFT, true);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, true);
+
+ // Horizontal expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.CENTER_HORIZONTAL, true);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, true);
+ }
+
+ @Test
+ public void updateGravity_move_regular_invalid() {
+ assumeTelevision();
+ int gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ mTvPipBoundsState.setTvPipGravity(gravity);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, gravity, false);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, gravity, false);
+
+ gravity = Gravity.BOTTOM | Gravity.LEFT;
+ mTvPipBoundsState.setTvPipGravity(gravity);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, gravity, false);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, gravity, false);
+
+ gravity = Gravity.TOP | Gravity.LEFT;
+ mTvPipBoundsState.setTvPipGravity(gravity);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, gravity, false);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, gravity, false);
+
+ gravity = Gravity.TOP | Gravity.RIGHT;
+ mTvPipBoundsState.setTvPipGravity(gravity);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, gravity, false);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, gravity, false);
+ }
+
+ @Test
+ public void updateGravity_move_expanded_invalid() {
+ assumeTelevision();
+ mTvPipBoundsState.setTvPipExpanded(true);
+
+ // Vertical expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
+
+ mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.CENTER_VERTICAL | Gravity.LEFT, false);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.CENTER_VERTICAL | Gravity.LEFT, false);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.CENTER_VERTICAL | Gravity.LEFT, false);
+
+ // Horizontal expanded PiP.
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
+
+ mTvPipBoundsState.setTvPipGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.CENTER_HORIZONTAL, false);
+ moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, false);
+ moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, false);
+ }
+
+ @Test
+ public void previousCollapsedGravity_defaultValue() {
+ assumeTelevision();
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ mTvPipBoundsState.getDefaultGravity());
+ setRTL(true);
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ mTvPipBoundsState.getDefaultGravity());
+ }
+
+ @Test
+ public void previousCollapsedGravity_changes_on_RTL() {
+ assumeTelevision();
+ mTvPipBoundsState.setTvPipPreviousCollapsedGravity(Gravity.TOP | Gravity.LEFT);
+ setRTL(true);
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ Gravity.TOP | Gravity.RIGHT);
+ setRTL(false);
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ Gravity.TOP | Gravity.LEFT);
+
+ mTvPipBoundsState.setTvPipPreviousCollapsedGravity(Gravity.BOTTOM | Gravity.RIGHT);
+ setRTL(true);
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ Gravity.BOTTOM | Gravity.LEFT);
+ setRTL(false);
+ assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(),
+ Gravity.BOTTOM | Gravity.RIGHT);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
index 0fcc5cf384c9..aedf65ddc269 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
@@ -21,23 +21,25 @@ import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.util.Size
import android.view.Gravity
-import org.junit.runner.RunWith
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
+import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
-import org.junit.Before
-import org.junit.Test
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
-class TvPipKeepClearAlgorithmTest {
+class TvPipKeepClearAlgorithmTest : ShellTestCase() {
private val DEFAULT_PIP_SIZE = Size(384, 216)
- private val EXPANDED_WIDE_PIP_SIZE = Size(384*2, 216)
+ private val EXPANDED_WIDE_PIP_SIZE = Size(384 * 2, 216)
+ private val EXPANDED_TALL_PIP_SIZE = Size(384, 216 * 4)
private val DASHBOARD_WIDTH = 484
private val BOTTOM_SHEET_HEIGHT = 524
private val STASH_OFFSET = 64
@@ -54,6 +56,9 @@ class TvPipKeepClearAlgorithmTest {
@Before
fun setup() {
+ if (!isTelevision) {
+ return
+ }
movementBounds = Rect(0, 0, SCREEN_SIZE.width, SCREEN_SIZE.height)
movementBounds.inset(SCREEN_EDGE_INSET, SCREEN_EDGE_INSET)
@@ -73,72 +78,84 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun testAnchorPosition_BottomRight() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
testAnchorPosition()
}
@Test
fun testAnchorPosition_TopRight() {
+ assumeTelevision()
gravity = Gravity.TOP or Gravity.RIGHT
testAnchorPosition()
}
@Test
fun testAnchorPosition_TopLeft() {
+ assumeTelevision()
gravity = Gravity.TOP or Gravity.LEFT
testAnchorPosition()
}
@Test
fun testAnchorPosition_BottomLeft() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.LEFT
testAnchorPosition()
}
@Test
fun testAnchorPosition_Right() {
+ assumeTelevision()
gravity = Gravity.RIGHT
testAnchorPosition()
}
@Test
fun testAnchorPosition_Left() {
+ assumeTelevision()
gravity = Gravity.LEFT
testAnchorPosition()
}
@Test
fun testAnchorPosition_Top() {
+ assumeTelevision()
gravity = Gravity.TOP
testAnchorPosition()
}
@Test
fun testAnchorPosition_Bottom() {
+ assumeTelevision()
gravity = Gravity.BOTTOM
testAnchorPosition()
}
@Test
fun testAnchorPosition_TopCenterHorizontal() {
+ assumeTelevision()
gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
testAnchorPosition()
}
@Test
fun testAnchorPosition_BottomCenterHorizontal() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
testAnchorPosition()
}
@Test
fun testAnchorPosition_RightCenterVertical() {
+ assumeTelevision()
gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
testAnchorPosition()
}
@Test
fun testAnchorPosition_LeftCenterVertical() {
+ assumeTelevision()
gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
testAnchorPosition()
}
@@ -152,6 +169,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_KeepClearNotObstructing_StayAtAnchor() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.LEFT)
@@ -166,6 +184,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_UnrestrictedRightSidebar_PushedLeft() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
@@ -180,6 +199,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorTopRight_UnrestrictedRightSidebar_PushedLeft() {
+ assumeTelevision()
gravity = Gravity.TOP or Gravity.RIGHT
val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
@@ -194,6 +214,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomLeft_UnrestrictedRightSidebar_StayAtAnchor() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.LEFT
val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
@@ -208,6 +229,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottom_UnrestrictedRightSidebar_StayAtAnchor() {
+ assumeTelevision()
gravity = Gravity.BOTTOM
val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
@@ -222,6 +244,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun testExpanded_AnchorBottom_UnrestrictedRightSidebar_StayAtAnchor() {
+ assumeTelevision()
pipSize = EXPANDED_WIDE_PIP_SIZE
gravity = Gravity.BOTTOM
@@ -237,6 +260,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_RestrictedSmallBottomBar_PushedUp() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(96)
@@ -252,6 +276,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_RestrictedBottomSheet_StashDownAtAnchor() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -269,6 +294,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_UnrestrictedBottomSheet_PushUp() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -284,6 +310,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_UnrestrictedBottomSheet_RestrictedSidebar_StashAboveBottomSheet() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -309,6 +336,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_AnchorBottomRight_UnrestrictedBottomSheet_UnrestrictedSidebar_PushUpLeft() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -331,6 +359,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_Stashed_UnstashBoundsBecomeUnobstructed_Unstashes() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -361,6 +390,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_Stashed_UnstashBoundsStaysObstructed_DoesNotTriggerStash() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -392,6 +422,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_Stashed_UnstashBoundsObstructionChanges_UnstashTimeExtended() {
+ assumeTelevision()
gravity = Gravity.BOTTOM or Gravity.RIGHT
val bottomBar = makeBottomBar(BOTTOM_SHEET_HEIGHT)
@@ -431,6 +462,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_ExpandedPiPHeightExceedsMovementBounds_AtAnchor() {
+ assumeTelevision()
gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
pipSize = Size(DEFAULT_PIP_SIZE.width, SCREEN_SIZE.height)
testAnchorPosition()
@@ -438,6 +470,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_ExpandedPiPHeightExceedsMovementBounds_BottomBar_StashedUp() {
+ assumeTelevision()
gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
pipSize = Size(DEFAULT_PIP_SIZE.width, SCREEN_SIZE.height)
val bottomBar = makeBottomBar(96)
@@ -453,6 +486,7 @@ class TvPipKeepClearAlgorithmTest {
@Test
fun test_PipInsets() {
+ assumeTelevision()
val insets = Insets.of(-1, -2, -3, -4)
algorithm.setPipPermanentDecorInsets(insets)
@@ -485,6 +519,41 @@ class TvPipKeepClearAlgorithmTest {
testAnchorPositionWithInsets(insets)
}
+ @Test
+ fun test_AnchorRightExpandedPiP_UnrestrictedRightSidebar_PushedLeft() {
+ assumeTelevision()
+ pipSize = EXPANDED_TALL_PIP_SIZE
+ gravity = Gravity.RIGHT
+
+ val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
+ unrestrictedAreas.add(sidebar)
+
+ val expectedBounds = anchorBoundsOffsetBy(SCREEN_EDGE_INSET - sidebar.width() - PADDING, 0)
+
+ val placement = getActualPlacement()
+ assertEquals(expectedBounds, placement.bounds)
+ assertNotStashed(placement)
+ }
+
+ @Test
+ fun test_AnchorRightExpandedPiP_RestrictedRightSidebar_StashedRight() {
+ assumeTelevision()
+ assumeTelevision()
+ pipSize = EXPANDED_TALL_PIP_SIZE
+ gravity = Gravity.RIGHT
+
+ val sidebar = makeSideBar(DASHBOARD_WIDTH, Gravity.RIGHT)
+ restrictedAreas.add(sidebar)
+
+ val expectedBounds = getExpectedAnchorBounds()
+ expectedBounds.offsetTo(SCREEN_SIZE.width - STASH_OFFSET, expectedBounds.top)
+
+ val placement = getActualPlacement()
+ assertEquals(expectedBounds, placement.bounds)
+ assertEquals(STASH_TYPE_RIGHT, placement.stashType)
+ assertEquals(getExpectedAnchorBounds(), placement.unstashDestinationBounds)
+ }
+
private fun testAnchorPositionWithInsets(insets: Insets) {
var pipRect = Rect(0, 0, pipSize.width, pipSize.height)
pipRect.inset(insets)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index e5ae2962e6e4..11fda8bf7bbc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -249,7 +249,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
doReturn(WindowManagerGlobal.ADD_OKAY).when(session).addToDisplay(
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
- any() /* requestedVisibility */, any() /* outInputChannel */,
+ anyInt() /* requestedVisibleTypes */, any() /* outInputChannel */,
any() /* outInsetsState */, any() /* outActiveControls */,
any() /* outAttachedFrame */, any() /* outSizeCompatScale */);
TaskSnapshotWindow mockSnapshotWindow = TaskSnapshotWindow.create(windowInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
deleted file mode 100644
index 3de50bb60470..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2017 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.startingsurface;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-
-import android.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorSpace;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.view.InsetsState;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.TaskSnapshot;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestShellExecutor;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for {@link TaskSnapshotWindow}.
- *
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TaskSnapshotWindowTest extends ShellTestCase {
-
- private TaskSnapshotWindow mWindow;
-
- private void setupSurface(int width, int height) {
- setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, width, height));
- }
-
- private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
- int windowFlags, Rect taskBounds) {
- // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
- // this behavior set the taskSize to be the same as the taskBounds width and height. The
- // taskBounds passed here are assumed to be the same task bounds as when the snapshot was
- // taken. We assume there is no aspect ratio mismatch between the screenshot and the
- // taskBounds
- assertEquals(width, taskBounds.width());
- assertEquals(height, taskBounds.height());
- Point taskSize = new Point(taskBounds.width(), taskBounds.height());
-
- final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets);
- mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test",
- createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
- 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
- taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD,
- new InsetsState(), null /* clearWindow */, new TestShellExecutor());
- }
-
- private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
- Rect contentInsets) {
- final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
- 1, HardwareBuffer.USAGE_CPU_READ_RARELY);
- return new TaskSnapshot(
- System.currentTimeMillis(),
- new ComponentName("", ""), buffer,
- ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, taskSize, contentInsets, new Rect() /* letterboxInsets */,
- false, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
- 0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
- }
-
- private static TaskDescription createTaskDescription(int background, int statusBar,
- int navigationBar) {
- final TaskDescription td = new TaskDescription();
- td.setBackgroundColor(background);
- td.setStatusBarColor(statusBar);
- td.setNavigationBarColor(navigationBar);
- return td;
- }
-
- @Test
- public void fillEmptyBackground_fillHorizontally() {
- setupSurface(200, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(200);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
- verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_fillVertically() {
- setupSurface(100, 200);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(200);
- mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
- verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_fillBoth() {
- setupSurface(200, 200);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(200);
- when(mockCanvas.getHeight()).thenReturn(200);
- mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
- verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
- verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
- }
-
- @Test
- public void fillEmptyBackground_dontFill_sameSize() {
- setupSurface(100, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void fillEmptyBackground_dontFill_bitmapLarger() {
- setupSurface(100, 100);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void testCalculateSnapshotCrop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 100, 90), mWindow.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_taskNotOnTop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
- assertEquals(new Rect(0, 10, 100, 90), mWindow.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarLeft() {
- setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(10, 0, 100, 100), mWindow.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_navBarRight() {
- setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(0, 0, 90, 100), mWindow.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotCrop_waterfall() {
- setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
- assertEquals(new Rect(5, 0, 95, 90), mWindow.calculateSnapshotCrop());
- }
-
- @Test
- public void testCalculateSnapshotFrame() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 0, 10);
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- assertEquals(new Rect(0, 0, 100, 80),
- mWindow.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
- }
-
- @Test
- public void testCalculateSnapshotFrame_navBarLeft() {
- setupSurface(100, 100);
- final Rect insets = new Rect(10, 10, 0, 0);
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- assertEquals(new Rect(10, 0, 100, 90),
- mWindow.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
- }
-
- @Test
- public void testCalculateSnapshotFrame_waterfall() {
- setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
- final Rect insets = new Rect(0, 10, 0, 10);
- mWindow.setFrames(new Rect(5, 0, 95, 100), insets);
- assertEquals(new Rect(0, 0, 90, 90),
- mWindow.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
- }
-
- @Test
- public void testDrawStatusBarBackground() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 10, 0);
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100));
- verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
- }
-
- @Test
- public void testDrawStatusBarBackground_nullFrame() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 10, 0);
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawStatusBarBackground(mockCanvas, null);
- verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
- }
-
- @Test
- public void testDrawStatusBarBackground_nope() {
- setupSurface(100, 100);
- final Rect insets = new Rect(0, 10, 10, 0);
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100));
- verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground() {
- final Rect insets = new Rect(0, 10, 0, 10);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground_left() {
- final Rect insets = new Rect(10, 10, 0, 0);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
- }
-
- @Test
- public void testDrawNavigationBarBackground_right() {
- final Rect insets = new Rect(0, 10, 10, 0);
- setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- new Rect(0, 0, 100, 100));
- mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
- final Canvas mockCanvas = mock(Canvas.class);
- when(mockCanvas.getWidth()).thenReturn(100);
- when(mockCanvas.getHeight()).thenReturn(100);
- mWindow.drawNavigationBarBackground(mockCanvas);
- verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
- }
-}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index c80fb188e70f..b1f327c94f8e 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -33,6 +33,7 @@ license {
cc_defaults {
name: "libandroidfw_defaults",
+ cpp_std: "gnu++2b",
cflags: [
"-Werror",
"-Wunreachable-code",
@@ -60,6 +61,7 @@ cc_library {
"AssetManager2.cpp",
"AssetsProvider.cpp",
"AttributeResolution.cpp",
+ "BigBuffer.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
"Idmap.cpp",
@@ -69,9 +71,11 @@ cc_library {
"misc.cpp",
"ObbFile.cpp",
"PosixUtils.cpp",
+ "ResourceTimer.cpp",
"ResourceTypes.cpp",
"ResourceUtils.cpp",
"StreamingZipInflater.cpp",
+ "StringPool.cpp",
"TypeWrappers.cpp",
"Util.cpp",
"ZipFileRO.cpp",
@@ -161,6 +165,7 @@ cc_test {
"tests/AssetManager2_test.cpp",
"tests/AttributeFinder_test.cpp",
"tests/AttributeResolution_test.cpp",
+ "tests/BigBuffer_test.cpp",
"tests/ByteBucketArray_test.cpp",
"tests/Config_test.cpp",
"tests/ConfigDescription_test.cpp",
@@ -169,10 +174,12 @@ cc_test {
"tests/Idmap_test.cpp",
"tests/LoadedArsc_test.cpp",
"tests/Locale_test.cpp",
+ "tests/ResourceTimer_test.cpp",
"tests/ResourceUtils_test.cpp",
"tests/ResTable_test.cpp",
"tests/Split_test.cpp",
"tests/StringPiece_test.cpp",
+ "tests/StringPool_test.cpp",
"tests/Theme_test.cpp",
"tests/TypeWrappers_test.cpp",
"tests/ZipUtils_test.cpp",
@@ -204,6 +211,8 @@ cc_test {
"tests/data/**/*.apk",
"tests/data/**/*.arsc",
"tests/data/**/*.idmap",
+ ":FrameworkResourcesSparseTestApp",
+ ":FrameworkResourcesNotSparseTestApp",
],
test_suites: ["device-tests"],
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 2beb33abe782..15aaae25f754 100755..100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -18,6 +18,7 @@
#include "android-base/errors.h"
#include "android-base/logging.h"
+#include "android-base/utf8.h"
namespace android {
@@ -83,15 +84,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
return {};
}
+ std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC));
std::unique_ptr<AssetsProvider> overlay_assets;
- const std::string overlay_path(loaded_idmap->OverlayApkPath());
- if (IsFabricatedOverlay(overlay_path)) {
+ if (IsFabricatedOverlay(fd)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
// are defined inline in the idmap.
- overlay_assets = EmptyAssetsProvider::Create(overlay_path);
+ overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path));
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(overlay_path, flags);
+ overlay_assets = ZipAssetsProvider::Create(std::move(overlay_path), flags, std::move(fd));
}
if (overlay_assets == nullptr) {
return {};
@@ -141,6 +143,9 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_
return {};
}
loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
+ } else if (loaded_idmap != nullptr &&
+ IsFabricatedOverlay(std::string(loaded_idmap->OverlayApkPath()))) {
+ loaded_arsc = LoadedArsc::Load(loaded_idmap.get());
} else {
loaded_arsc = LoadedArsc::CreateEmpty();
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 400829e15364..68f5e4a88c7e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -22,6 +22,7 @@
#include <iterator>
#include <map>
#include <set>
+#include <span>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
@@ -43,28 +44,19 @@ namespace {
using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
+/* NOTE: table_entry has been verified in LoadedPackage::GetEntryFromOffset(),
+ * and so access to ->value() and ->map_entry() are safe here
+ */
base::expected<EntryValue, IOError> GetEntryValue(
incfs::verified_map_ptr<ResTable_entry> table_entry) {
- const uint16_t entry_size = dtohs(table_entry->size);
+ const uint16_t entry_size = table_entry->size();
// Check if the entry represents a bag value.
- if (entry_size >= sizeof(ResTable_map_entry) &&
- (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
- const auto map_entry = table_entry.convert<ResTable_map_entry>();
- if (!map_entry) {
- return base::unexpected(IOError::PAGES_MISSING);
- }
- return map_entry.verified();
+ if (entry_size >= sizeof(ResTable_map_entry) && table_entry->is_complex()) {
+ return table_entry.convert<ResTable_map_entry>().verified();
}
- // The entry represents a non-bag value.
- const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
- if (!entry_value) {
- return base::unexpected(IOError::PAGES_MISSING);
- }
- Res_value value;
- value.copyFrom_dtoh(entry_value.value());
- return value;
+ return table_entry->value();
}
} // namespace
@@ -120,7 +112,7 @@ void AssetManager2::BuildDynamicRefTable() {
// A mapping from path of apk assets that could be target packages of overlays to the runtime
// package id of its first loaded package. Overlays currently can only override resources in the
// first package in the target resource table.
- std::unordered_map<std::string, uint8_t> target_assets_package_ids;
+ std::unordered_map<std::string_view, uint8_t> target_assets_package_ids;
// Overlay resources are not directly referenced by an application so their resource ids
// can change throughout the application's lifetime. Assign overlay package ids last.
@@ -143,7 +135,7 @@ void AssetManager2::BuildDynamicRefTable() {
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
// The target package must precede the overlay package in the apk assets paths in order
// to take effect.
- auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
+ auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath());
if (iter == target_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
@@ -188,7 +180,7 @@ void AssetManager2::BuildDynamicRefTable() {
if (overlay_ref_table != nullptr) {
// If this package is from an overlay, use a dynamic reference table that can rewrite
// overlay resource ids to their corresponding target resource ids.
- new_group.dynamic_ref_table = overlay_ref_table;
+ new_group.dynamic_ref_table = std::move(overlay_ref_table);
}
DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
@@ -196,9 +188,9 @@ void AssetManager2::BuildDynamicRefTable() {
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
- // Add the package and to the set of packages with the same ID.
+ // Add the package to the set of packages with the same ID.
PackageGroup* package_group = &package_groups_[idx];
- package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
+ package_group->packages_.emplace_back().loaded_package_ = package.get();
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
// Add the package name -> build time ID mappings.
@@ -210,30 +202,39 @@ void AssetManager2::BuildDynamicRefTable() {
if (auto apk_assets_path = apk_assets->GetPath()) {
// Overlay target ApkAssets must have been created using path based load apis.
- target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id));
+ target_assets_package_ids.emplace(*apk_assets_path, package_id);
}
}
}
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
- const auto package_groups_end = package_groups_.end();
- for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
- const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
- for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
- iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
- iter->dynamic_ref_table->mAssignedPackageId);
-
- // Add the alias resources to the dynamic reference table of every package group. Since
- // staging aliases can only be defined by the framework package (which is not a shared
- // library), the compile-time package id of the framework is the same across all packages
- // that compile against the framework.
- for (const auto& package : iter->packages_) {
- for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
- iter2->dynamic_ref_table->addAlias(entry.first, entry.second);
- }
- }
+ DynamicRefTable::AliasMap aliases;
+ for (const auto& group : package_groups_) {
+ const std::string& package_name = group.packages_[0].loaded_package_->GetPackageName();
+ const auto name_16 = String16(package_name.c_str(), package_name.size());
+ for (auto&& inner_group : package_groups_) {
+ inner_group.dynamic_ref_table->addMapping(name_16,
+ group.dynamic_ref_table->mAssignedPackageId);
+ }
+
+ for (const auto& package : group.packages_) {
+ const auto& package_aliases = package.loaded_package_->GetAliasResourceIdMap();
+ aliases.insert(aliases.end(), package_aliases.begin(), package_aliases.end());
}
}
+
+ if (!aliases.empty()) {
+ std::sort(aliases.begin(), aliases.end(), [](auto&& l, auto&& r) { return l.first < r.first; });
+
+ // Add the alias resources to the dynamic reference table of every package group. Since
+ // staging aliases can only be defined by the framework package (which is not a shared
+ // library), the compile-time package id of the framework is the same across all packages
+ // that compile against the framework.
+ for (auto& group : std::span(package_groups_.data(), package_groups_.size() - 1)) {
+ group.dynamic_ref_table->setAliases(aliases);
+ }
+ package_groups_.back().dynamic_ref_table->setAliases(std::move(aliases));
+ }
}
void AssetManager2::DumpToLog() const {
@@ -326,7 +327,7 @@ const std::unordered_map<std::string, std::string>*
return &loaded_package->GetOverlayableMap();
}
-bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name,
+bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name,
std::string* out) const {
uint8_t package_id = 0U;
for (const auto& apk_assets : apk_assets_) {
@@ -373,7 +374,7 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_
const std::string name = ToFormattedResourceString(*res_name);
output.append(base::StringPrintf(
"resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
- name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+ name.c_str(), info->name.data(), info->actor.data(), info->policy_flags));
}
}
}
@@ -501,7 +502,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con
continue;
}
- auto func = [&](const StringPiece& name, FileType type) {
+ auto func = [&](StringPiece name, FileType type) {
AssetDir::FileInfo info;
info.setFileName(String8(name.data(), name.size()));
info.setFileType(type);
@@ -611,7 +612,21 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
}
if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
- result->entry = overlay_entry.GetInlineValue();
+ 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;
@@ -800,17 +815,12 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
return base::unexpected(std::nullopt);
}
- auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
- if (!best_entry_result.has_value()) {
- return base::unexpected(best_entry_result.error());
- }
-
- const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
- if (!best_entry) {
- return base::unexpected(IOError::PAGES_MISSING);
+ auto best_entry_verified = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+ if (!best_entry_verified.has_value()) {
+ return base::unexpected(best_entry_verified.error());
}
- const auto entry = GetEntryValue(best_entry.verified());
+ const auto entry = GetEntryValue(*best_entry_verified);
if (!entry.has_value()) {
return base::unexpected(entry.error());
}
@@ -823,7 +833,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
.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->key.index),
+ (*best_entry_verified)->key()),
.dynamic_ref_table = package_group.dynamic_ref_table.get(),
};
}
@@ -1054,7 +1064,7 @@ 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, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, std::move(found_resids));
return bag;
}
@@ -1271,7 +1281,7 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
return result;
}
-static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) {
+static bool Utf8ToUtf16(StringPiece str, std::u16string* out) {
ssize_t len =
utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false);
if (len < 0) {
@@ -1346,22 +1356,22 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
void AssetManager2::RebuildFilterList() {
for (PackageGroup& group : package_groups_) {
- for (ConfiguredPackage& impl : group.packages_) {
- // Destroy it.
- impl.filtered_configs_.~ByteBucketArray();
-
- // Re-create it.
- new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
-
+ for (ConfiguredPackage& package : group.packages_) {
+ package.filtered_configs_.forEachItem([](auto, auto& fcg) { fcg.type_entries.clear(); });
// Create the filters here.
- impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
- FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_id - 1);
+ 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_)) {
- group.type_entries.push_back(&type_entry);
+ if (!group) {
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
+ }
+ group->type_entries.push_back(&type_entry);
}
}
});
+ package.filtered_configs_.trimBuckets(
+ [](const auto& fcg) { return fcg.type_entries.empty(); });
}
}
}
@@ -1402,30 +1412,34 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const
std::unique_ptr<Theme> AssetManager2::NewTheme() {
constexpr size_t kInitialReserveSize = 32;
auto theme = std::unique_ptr<Theme>(new Theme(this));
+ theme->keys_.reserve(kInitialReserveSize);
theme->entries_.reserve(kInitialReserveSize);
return theme;
}
+void AssetManager2::ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags) const {
+ for (const PackageGroup& package_group : package_groups_) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
+ return;
+ }
+ }
+}
+
Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {
}
Theme::~Theme() = default;
struct Theme::Entry {
- uint32_t attr_res_id;
ApkAssetsCookie cookie;
uint32_t type_spec_flags;
Res_value value;
};
-namespace {
-struct ThemeEntryKeyComparer {
- bool operator() (const Theme::Entry& entry, uint32_t attr_res_id) const noexcept {
- return entry.attr_res_id < attr_res_id;
- }
-};
-} // namespace
-
base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
@@ -1454,19 +1468,20 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid,
continue;
}
- Theme::Entry new_entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id,
- ThemeEntryKeyComparer{});
- if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), attr_res_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
+ if (key_it != keys_.end() && *key_it == attr_res_id) {
if (is_undefined) {
// DATA_NULL_UNDEFINED clears the value of the attribute in the theme only when `force` is
- /// true.
+ // true.
+ keys_.erase(key_it);
entries_.erase(entry_it);
} else if (force) {
- *entry_it = new_entry;
+ *entry_it = Entry{it->cookie, (*bag)->type_spec_flags, it->value};
}
} else {
- entries_.insert(entry_it, new_entry);
+ keys_.insert(key_it, attr_res_id);
+ entries_.insert(entry_it, Entry{it->cookie, (*bag)->type_spec_flags, it->value});
}
}
return {};
@@ -1477,6 +1492,7 @@ void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t*
ATRACE_NAME("Theme::Rebase");
// Reset the entries without changing the vector capacity to prevent reallocations during
// ApplyStyle.
+ keys_.clear();
entries_.clear();
asset_manager_ = am;
for (size_t i = 0; i < style_count; i++) {
@@ -1485,16 +1501,14 @@ void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t*
}
std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
-
constexpr const uint32_t kMaxIterations = 20;
uint32_t type_spec_flags = 0u;
for (uint32_t i = 0; i <= kMaxIterations; i++) {
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), resid,
- ThemeEntryKeyComparer{});
- if (entry_it == entries_.end() || entry_it->attr_res_id != resid) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), resid);
+ if (key_it == keys_.end() || *key_it != resid) {
return std::nullopt;
}
-
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
type_spec_flags |= entry_it->type_spec_flags;
if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) {
resid = entry_it->value.data;
@@ -1528,6 +1542,7 @@ base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
}
void Theme::Clear() {
+ keys_.clear();
entries_.clear();
}
@@ -1539,18 +1554,19 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
type_spec_flags_ = source.type_spec_flags_;
if (asset_manager_ == source.asset_manager_) {
+ keys_ = source.keys_;
entries_ = source.entries_;
} else {
- std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
- typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
- std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+ std::unordered_map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+ using SourceToDestinationRuntimePackageMap = std::unordered_map<int, int>;
+ std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
// Determine which ApkAssets are loaded in both theme AssetManagers.
- const auto src_assets = source.asset_manager_->GetApkAssets();
+ const auto& src_assets = source.asset_manager_->GetApkAssets();
for (size_t i = 0; i < src_assets.size(); i++) {
const ApkAssets* src_asset = src_assets[i];
- const auto dest_assets = asset_manager_->GetApkAssets();
+ const auto& dest_assets = asset_manager_->GetApkAssets();
for (size_t j = 0; j < dest_assets.size(); j++) {
const ApkAssets* dest_asset = dest_assets[j];
if (src_asset != dest_asset) {
@@ -1571,15 +1587,17 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
}
src_to_dest_asset_cookies.insert(std::make_pair(i, j));
- src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
+ src_asset_cookie_id_map.insert(std::make_pair(i, std::move(package_map)));
break;
}
}
// Reset the data in the destination theme.
+ keys_.clear();
entries_.clear();
- for (const auto& entry : source.entries_) {
+ for (size_t i = 0, size = source.entries_.size(); i != size; ++i) {
+ const auto& entry = source.entries_[i];
bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE
|| entry.value.dataType == Res_value::TYPE_REFERENCE
|| entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
@@ -1619,13 +1637,15 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
}
}
+ const auto source_res_id = source.keys_[i];
+
// The package id of the attribute needs to be rewritten to the package id of the
// attribute in the destination.
- int attribute_dest_package_id = get_package_id(entry.attr_res_id);
+ int attribute_dest_package_id = get_package_id(source_res_id);
if (attribute_dest_package_id != 0x01) {
// Find the cookie of the attribute resource id in the source AssetManager
base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
- source.asset_manager_->FindEntry(entry.attr_res_id, 0 /* density_override */ ,
+ source.asset_manager_->FindEntry(source_res_id, 0 /* density_override */ ,
true /* stop_at_first_match */,
true /* ignore_configuration */);
if (UNLIKELY(IsIOError(attribute_entry_result))) {
@@ -1649,16 +1669,15 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
attribute_dest_package_id = attribute_dest_package->second;
}
- auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(entry.attr_res_id),
- get_entry_id(entry.attr_res_id));
- Theme::Entry new_entry{dest_attr_id, data_dest_cookie, entry.type_spec_flags,
- Res_value{.dataType = entry.value.dataType,
- .data = attribute_data}};
-
+ auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(source_res_id),
+ get_entry_id(source_res_id));
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), dest_attr_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
// Since the entries were cleared, the attribute resource id has yet been mapped to any value.
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), dest_attr_id,
- ThemeEntryKeyComparer{});
- entries_.insert(entry_it, new_entry);
+ keys_.insert(key_it, dest_attr_id);
+ entries_.insert(entry_it, Entry{data_dest_cookie, entry.type_spec_flags,
+ Res_value{.dataType = entry.value.dataType,
+ .data = attribute_data}});
}
}
return {};
@@ -1666,9 +1685,11 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
void Theme::Dump() const {
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
- for (auto& entry : entries_) {
+ for (size_t i = 0, size = keys_.size(); i != size; ++i) {
+ auto res_id = keys_[i];
+ const auto& entry = entries_[i];
LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
- entry.attr_res_id, entry.value.data, entry.value.dataType,
+ res_id, entry.value.data, entry.value.dataType,
entry.cookie);
}
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index bce34d37c90b..2d3c06506a1f 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -73,9 +73,6 @@ std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
(path != nullptr) ? base::unique_fd(-1) : std::move(fd));
}
-ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
- : value_(std::forward<std::string>(value)), is_path_(is_path) {}
-
const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
return is_path_ ? &value_ : nullptr;
}
@@ -84,34 +81,42 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
return value_;
}
+void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const {
+ ::CloseArchive(a);
+}
+
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
package_property_t flags, time_t last_mod_time)
- : zip_handle_(handle, ::CloseArchive),
- name_(std::forward<PathOrDebugName>(path)),
+ : zip_handle_(handle),
+ name_(std::move(path)),
flags_(flags),
last_mod_time_(last_mod_time) {}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
- package_property_t flags) {
+ package_property_t flags,
+ base::unique_fd fd) {
+ const auto released_fd = fd.ok() ? fd.release() : -1;
ZipArchiveHandle handle;
- if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ if (int32_t result = released_fd < 0 ? OpenArchive(path.c_str(), &handle)
+ : OpenArchiveFd(released_fd, path.c_str(), &handle)) {
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
CloseArchive(handle);
return {};
}
struct stat sb{.st_mtime = -1};
- if (stat(path.c_str(), &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to stat file '" << path << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(path.c_str())) {
+ if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ PLOG(WARNING) << "Failed to stat file '" << path << "'";
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
- true /* is_path */}, flags, sb.st_mtime));
+ new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -133,17 +138,19 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
}
struct stat sb{.st_mtime = -1};
- if (fstat(released_fd, &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(released_fd)) {
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name
+ << "': " << base::SystemErrorCodeToString(errno);
+ }
}
- return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
- false /* is_path */}, flags, sb.st_mtime));
+ return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
+ handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -210,9 +217,9 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
return asset;
}
-bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f)
- const {
+bool ZipAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
std::string root_path_full = root_path;
if (root_path_full.back() != '/') {
root_path_full += '/';
@@ -238,8 +245,7 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
if (!leaf_file_path.empty()) {
auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ std::string dir(leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)));
dirs.insert(std::move(dir));
} else {
f(leaf_file_path, kFileTypeRegular);
@@ -277,6 +283,9 @@ const std::string& ZipAssetsProvider::GetDebugName() const {
}
bool ZipAssetsProvider::IsUpToDate() const {
+ if (last_mod_time_ == -1) {
+ return true;
+ }
struct stat sb{};
if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
// If fstat fails on the zip archive, return true so the zip archive the resource system does
@@ -287,10 +296,10 @@ bool ZipAssetsProvider::IsUpToDate() const {
}
DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
- : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+ : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
- struct stat sb{};
+ struct stat sb;
const int result = stat(path.c_str(), &sb);
if (result == -1) {
LOG(ERROR) << "Failed to find directory '" << path << "'.";
@@ -302,12 +311,13 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st
return nullptr;
}
- if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ if (path.back() != OS_PATH_SEPARATOR) {
path += OS_PATH_SEPARATOR;
}
- return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
- sb.st_mtime));
+ const bool isReadonly = isReadonlyFilesystem(path.c_str());
+ return std::unique_ptr<DirectoryAssetsProvider>(
+ new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
}
std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -324,8 +334,7 @@ std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string&
bool DirectoryAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */)
- const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -338,7 +347,10 @@ const std::string& DirectoryAssetsProvider::GetDebugName() const {
}
bool DirectoryAssetsProvider::IsUpToDate() const {
- struct stat sb{};
+ if (last_mod_time_ == -1) {
+ return true;
+ }
+ struct stat sb;
if (stat(dir_.c_str(), &sb) < 0) {
// If stat fails on the zip archive, return true so the zip archive the resource system does
// attempt to refresh the ApkAsset.
@@ -349,8 +361,7 @@ bool DirectoryAssetsProvider::IsUpToDate() const {
MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary)
- : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
- secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ : primary_(std::move(primary)), secondary_(std::move(secondary)) {
debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath()
: secondary_->GetPath();
@@ -372,9 +383,9 @@ std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path
return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
}
-bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f)
- const {
+bool MultiAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
}
@@ -397,8 +408,8 @@ std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider({}));
}
-std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(const std::string& path) {
- return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(path));
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(std::string path) {
+ return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(std::move(path)));
}
std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
@@ -412,7 +423,7 @@ std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* p
bool EmptyAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -435,4 +446,4 @@ bool EmptyAssetsProvider::IsUpToDate() const {
return true;
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/androidfw/BigBuffer.cpp b/libs/androidfw/BigBuffer.cpp
new file mode 100644
index 000000000000..bedfc49a1b0d
--- /dev/null
+++ b/libs/androidfw/BigBuffer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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 <androidfw/BigBuffer.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "android-base/logging.h"
+
+namespace android {
+
+void* BigBuffer::NextBlockImpl(size_t size) {
+ if (!blocks_.empty()) {
+ Block& block = blocks_.back();
+ if (block.block_size_ - block.size >= size) {
+ void* out_buffer = block.buffer.get() + block.size;
+ block.size += size;
+ size_ += size;
+ return out_buffer;
+ }
+ }
+
+ const size_t actual_size = std::max(block_size_, size);
+
+ Block block = {};
+
+ // Zero-allocate the block's buffer.
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actual_size]());
+ CHECK(block.buffer);
+
+ block.size = size;
+ block.block_size_ = actual_size;
+
+ blocks_.push_back(std::move(block));
+ size_ += size;
+ return blocks_.back().buffer.get();
+}
+
+void* BigBuffer::NextBlock(size_t* out_size) {
+ if (!blocks_.empty()) {
+ Block& block = blocks_.back();
+ if (block.size != block.block_size_) {
+ void* out_buffer = block.buffer.get() + block.size;
+ size_t size = block.block_size_ - block.size;
+ block.size = block.block_size_;
+ size_ += size;
+ *out_size = size;
+ return out_buffer;
+ }
+ }
+
+ // Zero-allocate the block's buffer.
+ Block block = {};
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[block_size_]());
+ CHECK(block.buffer);
+ block.size = block_size_;
+ block.block_size_ = block_size_;
+ blocks_.push_back(std::move(block));
+ size_ += block_size_;
+ *out_size = block_size_;
+ return blocks_.back().buffer.get();
+}
+
+std::string BigBuffer::to_string() const {
+ std::string result;
+ for (const Block& block : blocks_) {
+ result.append(block.buffer.get(), block.buffer.get() + block.size);
+ }
+ return result;
+}
+
+} // namespace android
diff --git a/libs/androidfw/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp
index 19ead9583eb2..93a7d173cb97 100644
--- a/libs/androidfw/ConfigDescription.cpp
+++ b/libs/androidfw/ConfigDescription.cpp
@@ -637,7 +637,7 @@ static bool parseVersion(const char* name, ResTable_config* out) {
return true;
}
-bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) {
+bool ConfigDescription::Parse(StringPiece str, ConfigDescription* out) {
std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
ConfigDescription config;
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index efd1f6a25786..89835742c8ff 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -56,6 +56,8 @@ struct Idmap_header {
struct Idmap_data_header {
uint32_t target_entry_count;
uint32_t target_inline_entry_count;
+ uint32_t target_inline_entry_value_count;
+ uint32_t configuration_count;
uint32_t overlay_entry_count;
uint32_t string_pool_index_offset;
@@ -68,6 +70,12 @@ struct Idmap_target_entry {
struct Idmap_target_entry_inline {
uint32_t target_id;
+ uint32_t start_value_index;
+ uint32_t value_count;
+};
+
+struct Idmap_target_entry_inline_value {
+ uint32_t config_index;
Res_value value;
};
@@ -138,11 +146,15 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table)
: data_header_(data_header),
entries_(entries),
inline_entries_(inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
target_assigned_package_id_(target_assigned_package_id),
overlay_ref_table_(overlay_ref_table) { }
@@ -183,7 +195,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
if (inline_entry != end_inline_entry &&
(0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
- return Result(inline_entry->value);
+ std::map<ConfigDescription, Res_value> values_map;
+ for (int i = 0; i < inline_entry->value_count; i++) {
+ const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
+ const auto& config = configurations_[value.config_index];
+ values_map[config] = value.value;
+ }
+ return Result(std::move(values_map));
}
return {};
}
@@ -232,28 +250,29 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
}
} // namespace
-LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
- const Idmap_header* header,
+LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
- std::string_view overlay_apk_path,
- std::string_view target_apk_path)
- : header_(header),
- data_header_(data_header),
- target_entries_(target_entries),
- target_inline_entries_(target_inline_entries),
- overlay_entries_(overlay_entries),
- string_pool_(std::move(string_pool)),
- idmap_path_(std::move(idmap_path)),
- overlay_apk_path_(overlay_apk_path),
- target_apk_path_(target_apk_path),
- idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
-
-std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data) {
+ std::string_view overlay_apk_path, std::string_view target_apk_path)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ target_inline_entries_(target_inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
+ overlay_entries_(overlay_entries),
+ string_pool_(std::move(string_pool)),
+ idmap_path_(std::move(idmap_path)),
+ overlay_apk_path_(overlay_apk_path),
+ target_apk_path_(target_apk_path),
+ idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
+
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
@@ -303,6 +322,21 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
if (target_inline_entries == nullptr) {
return {};
}
+
+ auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
+ &data_ptr, &data_size, "target inline values",
+ dtohl(data_header->target_inline_entry_value_count));
+ if (target_inline_entry_values == nullptr) {
+ return {};
+ }
+
+ auto configurations = ReadType<ConfigDescription>(
+ &data_ptr, &data_size, "configurations",
+ dtohl(data_header->configuration_count));
+ if (configurations == nullptr) {
+ return {};
+ }
+
auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
dtohl(data_header->overlay_entry_count));
if (overlay_entries == nullptr) {
@@ -328,9 +362,9 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
// Can't use make_unique because LoadedIdmap constructor is private.
return std::unique_ptr<LoadedIdmap>(
- new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
- target_inline_entries, overlay_entries, std::move(idmap_string_pool),
- *target_path, *overlay_path));
+ 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));
}
bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 35b6170fae5b..c0fdfe25da21 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -88,7 +88,9 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
// Make sure that there is enough room for the entry offsets.
const size_t offsets_offset = dtohs(header->header.headerSize);
const size_t entries_offset = dtohl(header->entriesStart);
- const size_t offsets_length = sizeof(uint32_t) * entry_count;
+ const size_t offsets_length = header->flags & ResTable_type::FLAG_OFFSET16
+ ? sizeof(uint16_t) * entry_count
+ : sizeof(uint32_t) * entry_count;
if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.";
@@ -107,8 +109,8 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
return true;
}
-static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
- incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
+static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+VerifyResTableEntry(incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
// Check that the offset is aligned.
if (UNLIKELY(entry_offset & 0x03U)) {
LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
@@ -136,7 +138,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(IOError::PAGES_MISSING);
}
- const size_t entry_size = dtohs(entry->size);
+ const size_t entry_size = entry->size();
if (UNLIKELY(entry_size < sizeof(entry.value()))) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too small.";
@@ -149,6 +151,11 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(std::nullopt);
}
+ // If entry is compact, value is already encoded, and a compact entry
+ // cannot be a map_entry, we are done verifying
+ if (entry->is_compact())
+ return entry.verified();
+
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
@@ -192,7 +199,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
return base::unexpected(std::nullopt);
}
}
- return {};
+ return entry.verified();
}
LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -228,7 +235,7 @@ uint32_t LoadedPackage::iterator::operator*() const {
entryIndex_);
}
-base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
+base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
if (UNLIKELY(!entry_offset.has_value())) {
@@ -242,14 +249,13 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const size_t entry_count = dtohl(type_chunk->entryCount);
- const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+ const auto offsets = type_chunk.offset(dtohs(type_chunk->header.headerSize));
// Check if there is the desired entry in this type.
if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
// This is encoded as a sparse map, so perform a binary search.
bool error = false;
- auto sparse_indices = type_chunk.offset(offsets_offset)
- .convert<ResTable_sparseTypeEntry>().iterator();
+ auto sparse_indices = offsets.convert<ResTable_sparseTypeEntry>().iterator();
auto sparse_indices_end = sparse_indices + entry_count;
auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
[&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
@@ -284,26 +290,36 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
return base::unexpected(std::nullopt);
}
- const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
- if (UNLIKELY(!entry_offset_ptr)) {
- return base::unexpected(IOError::PAGES_MISSING);
+ uint32_t result;
+
+ if (type_chunk->flags & ResTable_type::FLAG_OFFSET16) {
+ const auto entry_offset_ptr = offsets.convert<uint16_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ result = offset_from16(entry_offset_ptr.value());
+ } else {
+ const auto entry_offset_ptr = offsets.convert<uint32_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ result = dtohl(entry_offset_ptr.value());
}
- const uint32_t value = dtohl(entry_offset_ptr.value());
- if (value == ResTable_type::NO_ENTRY) {
+ if (result == ResTable_type::NO_ENTRY) {
return base::unexpected(std::nullopt);
}
-
- return value;
+ return result;
}
-base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
- incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
+base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+LoadedPackage::GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk,
+ uint32_t offset) {
auto valid = VerifyResTableEntry(type_chunk, offset);
if (UNLIKELY(!valid.has_value())) {
return base::unexpected(valid.error());
}
- return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
+ return valid;
}
base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
@@ -376,31 +392,42 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
for (const auto& type_entry : type_spec->type_entries) {
const incfs::verified_map_ptr<ResTable_type>& type = type_entry.type;
- size_t entry_count = dtohl(type->entryCount);
- for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
- auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
- entry_idx;
- if (!entry_offset_ptr) {
- return base::unexpected(IOError::PAGES_MISSING);
- }
+ const size_t entry_count = dtohl(type->entryCount);
+ const auto entry_offsets = type.offset(dtohs(type->header.headerSize));
+ for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
uint32_t offset;
uint16_t res_idx;
if (type->flags & ResTable_type::FLAG_SPARSE) {
- auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+ auto sparse_entry = entry_offsets.convert<ResTable_sparseTypeEntry>() + entry_idx;
+ if (!sparse_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
offset = dtohs(sparse_entry->offset) * 4u;
res_idx = dtohs(sparse_entry->idx);
+ } else if (type->flags & ResTable_type::FLAG_OFFSET16) {
+ auto entry = entry_offsets.convert<uint16_t>() + entry_idx;
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ offset = offset_from16(entry.value());
+ res_idx = entry_idx;
} else {
- offset = dtohl(entry_offset_ptr.value());
+ auto entry = entry_offsets.convert<uint32_t>() + entry_idx;
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ offset = dtohl(entry.value());
res_idx = entry_idx;
}
+
if (offset != ResTable_type::NO_ENTRY) {
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
if (!entry) {
return base::unexpected(IOError::PAGES_MISSING);
}
- if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
+ if (entry->key() == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
@@ -618,16 +645,16 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
std::string name;
- util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
+ util::ReadUtf16StringFromDevice(overlayable->name, std::size(overlayable->name), &name);
std::string actor;
- util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
-
- if (loaded_package->overlayable_map_.find(name) !=
- loaded_package->overlayable_map_.end()) {
- LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'.";
+ util::ReadUtf16StringFromDevice(overlayable->actor, std::size(overlayable->actor), &actor);
+ auto [name_to_actor_it, inserted] =
+ loaded_package->overlayable_map_.emplace(std::move(name), std::move(actor));
+ if (!inserted) {
+ LOG(ERROR) << "Multiple <overlayable> blocks with the same name '"
+ << name_to_actor_it->first << "'.";
return {};
}
- loaded_package->overlayable_map_.emplace(name, actor);
// Iterate over the overlayable policy chunks contained within the overlayable chunk data
ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
@@ -642,7 +669,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
return {};
}
-
if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
< dtohl(policy_header->entry_count)) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
@@ -664,8 +690,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
// Add the pairing of overlayable properties and resource ids to the package
OverlayableInfo overlayable_info {
- .name = name,
- .actor = actor,
+ .name = name_to_actor_it->first,
+ .actor = name_to_actor_it->second,
.policy_flags = policy_header->policy_flags
};
loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
@@ -709,6 +735,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
const auto entry_end = entry_begin + dtohl(lib_alias->count);
std::unordered_set<uint32_t> finalized_ids;
finalized_ids.reserve(entry_end - entry_begin);
+ loaded_package->alias_id_map_.reserve(entry_end - entry_begin);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
if (!entry_iter) {
LOG(ERROR) << "NULL ResTable_staged_alias_entry record??";
@@ -722,13 +749,20 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
auto staged_id = dtohl(entry_iter->stagedResId);
- auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
- if (!success) {
+ loaded_package->alias_id_map_.emplace_back(staged_id, finalized_id);
+ }
+
+ std::sort(loaded_package->alias_id_map_.begin(), loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first < r.first; });
+ const auto duplicate_it =
+ std::adjacent_find(loaded_package->alias_id_map_.begin(),
+ loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first == r.first; });
+ if (duplicate_it != loaded_package->alias_id_map_.end()) {
LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
- staged_id);
+ duplicate_it->first);
return {};
}
- }
} break;
default:
@@ -820,6 +854,13 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
return true;
}
+bool LoadedArsc::LoadStringPool(const LoadedIdmap* loaded_idmap) {
+ if (loaded_idmap != nullptr) {
+ global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
+ }
+ return true;
+}
+
std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
const size_t length,
const LoadedIdmap* loaded_idmap,
@@ -855,6 +896,16 @@ std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
return loaded_arsc;
}
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(const LoadedIdmap* loaded_idmap) {
+ ATRACE_NAME("LoadedArsc::Load");
+
+ // Not using make_unique because the constructor is private.
+ std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+ loaded_arsc->LoadStringPool(loaded_idmap);
+ return loaded_arsc;
+}
+
+
std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
return std::unique_ptr<LoadedArsc>(new LoadedArsc());
}
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index d87a3ce72177..272a988ec55a 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -66,7 +66,7 @@ static inline bool is_number(const std::string& str) {
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
-bool LocaleValue::InitFromFilterString(const StringPiece& str) {
+bool LocaleValue::InitFromFilterString(StringPiece str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
@@ -132,11 +132,11 @@ bool LocaleValue::InitFromFilterString(const StringPiece& str) {
return true;
}
-bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) {
+bool LocaleValue::InitFromBcp47Tag(StringPiece bcp47tag) {
return InitFromBcp47TagImpl(bcp47tag, '-');
}
-bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) {
+bool LocaleValue::InitFromBcp47TagImpl(StringPiece bcp47tag, const char separator) {
std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator);
if (subtags.size() == 1) {
set_language(subtags[0].c_str());
diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index 026912883a73..8ddc57240129 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -17,7 +17,7 @@
#ifdef _WIN32
// nothing to see here
#else
-#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -29,45 +29,42 @@
#include "androidfw/PosixUtils.h"
-namespace {
-
-std::unique_ptr<std::string> ReadFile(int fd) {
- std::unique_ptr<std::string> str(new std::string());
+static std::optional<std::string> ReadFile(int fd) {
+ std::string str;
char buf[1024];
ssize_t r;
while ((r = read(fd, buf, sizeof(buf))) > 0) {
- str->append(buf, r);
+ str.append(buf, r);
}
if (r != 0) {
- return nullptr;
+ return std::nullopt;
}
- return str;
-}
-
+ return std::move(str);
}
namespace android {
namespace util {
-std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv) {
- int stdout[2]; // stdout[0] read, stdout[1] write
+ProcResult ExecuteBinary(const std::vector<std::string>& argv) {
+ int stdout[2]; // [0] read, [1] write
if (pipe(stdout) != 0) {
- PLOG(ERROR) << "pipe";
- return nullptr;
+ PLOG(ERROR) << "out pipe";
+ return ProcResult{-1};
}
- int stderr[2]; // stdout[0] read, stdout[1] write
+ int stderr[2]; // [0] read, [1] write
if (pipe(stderr) != 0) {
- PLOG(ERROR) << "pipe";
+ PLOG(ERROR) << "err pipe";
close(stdout[0]);
close(stdout[1]);
- return nullptr;
+ return ProcResult{-1};
}
auto gid = getgid();
auto uid = getuid();
- char const** argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1));
+ // better keep no C++ objects going into the child here
+ auto argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1));
for (size_t i = 0; i < argv.size(); i++) {
argv0[i] = argv[i].c_str();
}
@@ -76,8 +73,12 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv)
switch (pid) {
case -1: // error
free(argv0);
+ close(stdout[0]);
+ close(stdout[1]);
+ close(stderr[0]);
+ close(stderr[1]);
PLOG(ERROR) << "fork";
- return nullptr;
+ return ProcResult{-1};
case 0: // child
if (setgid(gid) != 0) {
PLOG(ERROR) << "setgid";
@@ -109,17 +110,16 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv)
if (!WIFEXITED(status)) {
close(stdout[0]);
close(stderr[0]);
- return nullptr;
+ return ProcResult{-1};
}
- std::unique_ptr<ProcResult> result(new ProcResult());
- result->status = status;
- const auto out = ReadFile(stdout[0]);
- result->stdout_str = out ? *out : "";
+ ProcResult result(status);
+ auto out = ReadFile(stdout[0]);
+ result.stdout_str = out ? std::move(*out) : "";
close(stdout[0]);
- const auto err = ReadFile(stderr[0]);
- result->stderr_str = err ? *err : "";
+ auto err = ReadFile(stderr[0]);
+ result.stderr_str = err ? std::move(*err) : "";
close(stderr[0]);
- return result;
+ return std::move(result);
}
}
diff --git a/libs/androidfw/ResourceTimer.cpp b/libs/androidfw/ResourceTimer.cpp
new file mode 100644
index 000000000000..44128d9e4e3d
--- /dev/null
+++ b/libs/androidfw/ResourceTimer.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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 <unistd.h>
+#include <string.h>
+
+#include <map>
+#include <atomic>
+
+#include <utils/Log.h>
+#include <androidfw/ResourceTimer.h>
+
+// The following block allows compilation on windows, which does not have getuid().
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#define getuid() (getUidWindows_)
+#endif
+
+namespace android {
+
+namespace {
+
+#ifdef _WIN32
+// A temporary to confuse lint into thinking that getuid() on windows might return something other
+// than zero.
+int getUidWindows_ = 0;
+#endif
+
+// The number of nanoseconds in a microsecond.
+static const unsigned int US = 1000;
+// The number of nanoseconds in a second.
+static const unsigned int S = 1000 * 1000 * 1000;
+
+// Return the difference between two timespec values. The difference is in nanoseconds. If the
+// return value would exceed 2s (2^31 nanoseconds) then UINT_MAX is returned.
+unsigned int diffInNs(timespec const &a, timespec const &b) {
+ timespec r = { 0, 0 };
+ r.tv_nsec = a.tv_nsec - b.tv_nsec;
+ if (r.tv_nsec < 0) {
+ r.tv_sec = -1;
+ r.tv_nsec += S;
+ }
+ r.tv_sec = r.tv_sec + (a.tv_sec - b.tv_sec);
+ if (r.tv_sec > 2) return UINT_MAX;
+ unsigned int result = (r.tv_sec * S) + r.tv_nsec;
+ if (result > 2 * S) return UINT_MAX;
+ return result;
+}
+
+}
+
+ResourceTimer::ResourceTimer(Counter api)
+ : active_(enabled_.load()),
+ api_(api) {
+ if (active_) {
+ clock_gettime(CLOCK_MONOTONIC, &start_);
+ }
+}
+
+ResourceTimer::~ResourceTimer() {
+ record();
+}
+
+void ResourceTimer::enable() {
+ if (!enabled_.load()) counter_ = new GuardedTimer[ResourceTimer::counterSize];
+ enabled_.store(true);
+}
+
+void ResourceTimer::cancel() {
+ active_ = false;
+}
+
+void ResourceTimer::record() {
+ if (!active_) return;
+
+ struct timespec end;
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ // Get the difference in microseconds.
+ const unsigned int ticks = diffInNs(end, start_);
+ ScopedTimer t(counter_[toIndex(api_)]);
+ t->record(ticks);
+ active_ = false;
+}
+
+bool ResourceTimer::copy(int counter, Timer &dst, bool reset) {
+ ScopedTimer t(counter_[counter]);
+ if (t->count == 0) {
+ dst.reset();
+ if (reset) t->reset();
+ return false;
+ }
+ Timer::copy(dst, *t, reset);
+ return true;
+}
+
+void ResourceTimer::reset() {
+ for (int i = 0; i < counterSize; i++) {
+ ScopedTimer t(counter_[i]);
+ t->reset();
+ }
+}
+
+ResourceTimer::Timer::Timer() {
+ // Ensure newly-created objects are zeroed.
+ memset(buckets, 0, sizeof(buckets));
+ reset();
+}
+
+ResourceTimer::Timer::~Timer() {
+ for (int d = 0; d < MaxDimension; d++) {
+ delete[] buckets[d];
+ }
+}
+
+void ResourceTimer::Timer::freeBuckets() {
+ for (int d = 0; d < MaxDimension; d++) {
+ delete[] buckets[d];
+ buckets[d] = 0;
+ }
+}
+
+void ResourceTimer::Timer::reset() {
+ count = total = mintime = maxtime = 0;
+ memset(largest, 0, sizeof(largest));
+ memset(&pvalues, 0, sizeof(pvalues));
+ // Zero the histogram, keeping any allocated dimensions.
+ for (int d = 0; d < MaxDimension; d++) {
+ if (buckets[d] != 0) memset(buckets[d], 0, sizeof(int) * MaxBuckets);
+ }
+}
+
+void ResourceTimer::Timer::copy(Timer &dst, Timer &src, bool reset) {
+ dst.freeBuckets();
+ dst = src;
+ // Clean up the histograms.
+ if (reset) {
+ // Do NOT free the src buckets because they being used by dst.
+ memset(src.buckets, 0, sizeof(src.buckets));
+ src.reset();
+ } else {
+ for (int d = 0; d < MaxDimension; d++) {
+ if (src.buckets[d] != nullptr) {
+ dst.buckets[d] = new int[MaxBuckets];
+ memcpy(dst.buckets[d], src.buckets[d], sizeof(int) * MaxBuckets);
+ }
+ }
+ }
+}
+
+void ResourceTimer::Timer::record(int ticks) {
+ // Record that the event happened.
+ count++;
+
+ total += ticks;
+ if (mintime == 0 || ticks < mintime) mintime = ticks;
+ if (ticks > maxtime) maxtime = ticks;
+
+ // Do not add oversized events to the histogram.
+ if (ticks != UINT_MAX) {
+ for (int d = 0; d < MaxDimension; d++) {
+ if (ticks < range[d]) {
+ if (buckets[d] == 0) {
+ buckets[d] = new int[MaxBuckets];
+ memset(buckets[d], 0, sizeof(int) * MaxBuckets);
+ }
+ if (ticks < width[d]) {
+ // Special case: never write to bucket 0 because it complicates the percentile logic.
+ // However, this is always the smallest possible value to it is very unlikely to ever
+ // affect any of the percentile results.
+ buckets[d][1]++;
+ } else {
+ buckets[d][ticks / width[d]]++;
+ }
+ break;
+ }
+ }
+ }
+
+ // The list of largest times is sorted with the biggest value at index 0 and the smallest at
+ // index MaxLargest-1. The incoming tick count should be added to the array only if it is
+ // larger than the current value at MaxLargest-1.
+ if (ticks > largest[Timer::MaxLargest-1]) {
+ for (size_t i = 0; i < Timer::MaxLargest; i++) {
+ if (ticks > largest[i]) {
+ if (i < Timer::MaxLargest-1) {
+ for (size_t j = Timer::MaxLargest - 1; j > i; j--) {
+ largest[j] = largest[j-1];
+ }
+ }
+ largest[i] = ticks;
+ break;
+ }
+ }
+ }
+}
+
+void ResourceTimer::Timer::Percentile::compute(
+ int cumulative, int current, int count, int width, int time) {
+ nominal = time;
+ nominal_actual = (cumulative * 100) / count;
+ floor = nominal - width;
+ floor_actual = ((cumulative - current) * 100) / count;
+}
+
+void ResourceTimer::Timer::compute() {
+ memset(&pvalues, 0, sizeof(pvalues));
+
+ float l50 = count / 2.0;
+ float l90 = (count * 9.0) / 10.0;
+ float l95 = (count * 95.0) / 100.0;
+ float l99 = (count * 99.0) / 100.0;
+
+ int sum = 0;
+ for (int d = 0; d < MaxDimension; d++) {
+ if (buckets[d] == 0) continue;
+ for (int j = 0; j < MaxBuckets && sum < count; j++) {
+ // Empty buckets don't contribute to the answers. Skip them.
+ if (buckets[d][j] == 0) continue;
+ sum += buckets[d][j];
+ // A word on indexing. j is never zero in the following lines. buckets[0][0] corresponds
+ // to a delay of 0us, which cannot happen. buckets[n][0], for n > 0 overlaps a value in
+ // buckets[n-1], and the code would have stopped there.
+ if (sum >= l50 && pvalues.p50.nominal == 0) {
+ pvalues.p50.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l90 && pvalues.p90.nominal == 0) {
+ pvalues.p90.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l95 && pvalues.p95.nominal == 0) {
+ pvalues.p95.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l99 && pvalues.p99.nominal == 0) {
+ pvalues.p99.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ }
+ }
+}
+
+char const *ResourceTimer::toString(ResourceTimer::Counter counter) {
+ switch (counter) {
+ case Counter::GetResourceValue:
+ return "GetResourceValue";
+ case Counter::RetrieveAttributes:
+ return "RetrieveAttributes";
+ };
+ return "Unknown";
+}
+
+std::atomic<bool> ResourceTimer::enabled_(false);
+std::atomic<ResourceTimer::GuardedTimer *> ResourceTimer::counter_(nullptr);
+
+const int ResourceTimer::Timer::range[] = { 100 * US, 1000 * US, 10*1000 * US, 100*1000 * US };
+const int ResourceTimer::Timer::width[] = { 1 * US, 10 * US, 100 * US, 1000 * US };
+
+
+} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5e8a623d4205..1fed2067d16e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -33,7 +33,9 @@
#include <type_traits>
#include <vector>
+#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/utf8.h>
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -236,12 +238,23 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
}
bool IsFabricatedOverlay(const std::string& path) {
- std::ifstream fin(path);
+ return IsFabricatedOverlay(path.c_str());
+}
+
+bool IsFabricatedOverlay(const char* path) {
+ auto fd = base::unique_fd(base::utf8::open(path, O_RDONLY|O_CLOEXEC));
+ if (fd < 0) {
+ return false;
+ }
+ return IsFabricatedOverlay(fd);
+}
+
+bool IsFabricatedOverlay(base::borrowed_fd fd) {
uint32_t magic;
- if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) {
- return magic == kFabricatedOverlayMagic;
+ if (!base::ReadFullyAtOffset(fd, &magic, sizeof(magic), 0)) {
+ return false;
}
- return false;
+ return magic == kFabricatedOverlayMagic;
}
static bool assertIdmapHeader(const void* idmap, size_t size) {
@@ -4487,20 +4500,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
return err;
}
- if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+ if (entry.entry->map_entry()) {
if (!mayBeBag) {
ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
return BAD_VALUE;
}
- const Res_value* value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
-
- outValue->size = dtohs(value->size);
- outValue->res0 = value->res0;
- outValue->dataType = value->dataType;
- outValue->data = dtohl(value->data);
+ *outValue = entry.entry->value();
// The reference may be pointing to a resource in a shared library. These
// references have build-time generated package IDs. These ids may not match
@@ -4691,11 +4698,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
return err;
}
- const uint16_t entrySize = dtohs(entry.entry->size);
- const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
- const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
+ const uint16_t entrySize = entry.entry->size();
+ const ResTable_map_entry* map_entry = entry.entry->map_entry();
+ const uint32_t parent = map_entry ? dtohl(map_entry->parent.ident) : 0;
+ const uint32_t count = map_entry ? dtohl(map_entry->count) : 0;
size_t N = count;
@@ -4759,7 +4765,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
// Now merge in the new attributes...
size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
- + dtohs(entry.entry->size);
+ + entrySize;
const ResTable_map* map;
bag_entry* entries = (bag_entry*)(set+1);
size_t curEntry = 0;
@@ -5137,7 +5143,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const
continue;
}
- if (dtohl(entry->key.index) == (size_t) *ei) {
+ if (entry->key() == (size_t) *ei) {
uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
if (outTypeSpecFlags) {
Entry result;
@@ -6600,8 +6606,12 @@ status_t ResTable::getEntry(
// Entry does not exist.
continue;
}
-
- thisOffset = dtohl(eindex[realEntryIndex]);
+ if (thisType->flags & ResTable_type::FLAG_OFFSET16) {
+ auto eindex16 = reinterpret_cast<const uint16_t*>(eindex);
+ thisOffset = offset_from16(eindex16[realEntryIndex]);
+ } else {
+ thisOffset = dtohl(eindex[realEntryIndex]);
+ }
}
if (thisOffset == ResTable_type::NO_ENTRY) {
@@ -6651,8 +6661,8 @@ status_t ResTable::getEntry(
const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
- if (dtohs(entry->size) < sizeof(*entry)) {
- ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
+ if (entry->size() < sizeof(*entry)) {
+ ALOGW("ResTable_entry size 0x%zx is too small", entry->size());
return BAD_TYPE;
}
@@ -6663,7 +6673,7 @@ status_t ResTable::getEntry(
outEntry->specFlags = specFlags;
outEntry->package = bestPackage;
outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
- outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
+ outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, entry->key());
}
return NO_ERROR;
}
@@ -6880,7 +6890,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
const uint32_t typeSize = dtohl(type->header.size);
const size_t newEntryCount = dtohl(type->entryCount);
-
+ const size_t entrySize = type->flags & ResTable_type::FLAG_OFFSET16 ?
+ sizeof(uint16_t) : sizeof(uint32_t);
if (kDebugLoadTableNoisy) {
printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n",
(void*)(base-(const uint8_t*)chunk),
@@ -6888,9 +6899,9 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
dtohs(type->header.headerSize),
typeSize);
}
- if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) {
+ if (dtohs(type->header.headerSize)+(entrySize*newEntryCount) > typeSize) {
ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
- (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+ (void*)(dtohs(type->header.headerSize) + (entrySize*newEntryCount)),
typeSize);
return (mError=BAD_TYPE);
}
@@ -6991,11 +7002,10 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
- : mAssignedPackageId(packageId)
+ : mLookupTable()
+ , mAssignedPackageId(packageId)
, mAppAsLib(appAsLib)
{
- memset(mLookupTable, 0, sizeof(mLookupTable));
-
// Reserved package ids
mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
@@ -7076,10 +7086,6 @@ void DynamicRefTable::addMapping(uint8_t buildPackageId, uint8_t runtimePackageI
mLookupTable[buildPackageId] = runtimePackageId;
}
-void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
- mAliasId[stagedId] = finalizedId;
-}
-
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7089,11 +7095,12 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
return NO_ERROR;
}
- auto alias_id = mAliasId.find(res);
- if (alias_id != mAliasId.end()) {
+ const auto alias_it = std::lower_bound(mAliasId.begin(), mAliasId.end(), res,
+ [](const AliasMap::value_type& pair, uint32_t val) { return pair.first < val; });
+ if (alias_it != mAliasId.end() && alias_it->first == res) {
// Rewrite the resource id to its alias resource id. Since the alias resource id is a
// compile-time id, it still needs to be resolved further.
- res = alias_id->second;
+ res = alias_it->second;
}
if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
@@ -7653,6 +7660,9 @@ void ResTable::print(bool inclValues) const
if (type->flags & ResTable_type::FLAG_SPARSE) {
printf(" [sparse]");
}
+ if (type->flags & ResTable_type::FLAG_OFFSET16) {
+ printf(" [offset16]");
+ }
}
printf(":\n");
@@ -7684,7 +7694,13 @@ void ResTable::print(bool inclValues) const
thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
} else {
entryId = entryIndex;
- thisOffset = dtohl(eindex[entryIndex]);
+ if (type->flags & ResTable_type::FLAG_OFFSET16) {
+ const auto eindex16 =
+ reinterpret_cast<const uint16_t*>(eindex);
+ thisOffset = offset_from16(eindex16[entryIndex]);
+ } else {
+ thisOffset = dtohl(eindex[entryIndex]);
+ }
if (thisOffset == ResTable_type::NO_ENTRY) {
continue;
}
@@ -7734,7 +7750,7 @@ void ResTable::print(bool inclValues) const
continue;
}
- uintptr_t esize = dtohs(ent->size);
+ uintptr_t esize = ent->size();
if ((esize&0x3) != 0) {
printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
continue;
@@ -7746,30 +7762,27 @@ void ResTable::print(bool inclValues) const
}
const Res_value* valuePtr = NULL;
- const ResTable_map_entry* bagPtr = NULL;
+ const ResTable_map_entry* bagPtr = ent->map_entry();
Res_value value;
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+ if (bagPtr) {
printf("<bag>");
- bagPtr = (const ResTable_map_entry*)ent;
} else {
- valuePtr = (const Res_value*)
- (((const uint8_t*)ent) + esize);
- value.copyFrom_dtoh(*valuePtr);
+ value = ent->value();
printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
(int)value.dataType, (int)value.data,
(int)value.size, (int)value.res0);
}
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+ if (ent->flags() & ResTable_entry::FLAG_PUBLIC) {
printf(" (PUBLIC)");
}
printf("\n");
if (inclValues) {
- if (valuePtr != NULL) {
+ if (bagPtr == NULL) {
printf(" ");
print_value(typeConfigs->package, value);
- } else if (bagPtr != NULL) {
+ } else {
const int N = dtohl(bagPtr->count);
const uint8_t* baseMapPtr = (const uint8_t*)ent;
size_t mapOffset = esize;
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index 87fb2c038c9f..ccb61561578f 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -18,7 +18,7 @@
namespace android {
-bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
+bool ExtractResourceName(StringPiece str, StringPiece* out_package, StringPiece* out_type,
StringPiece* out_entry) {
*out_package = "";
*out_type = "";
@@ -33,16 +33,16 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
while (current != end) {
if (out_type->size() == 0 && *current == '/') {
has_type_separator = true;
- out_type->assign(start, current - start);
+ *out_type = StringPiece(start, current - start);
start = current + 1;
} else if (out_package->size() == 0 && *current == ':') {
has_package_separator = true;
- out_package->assign(start, current - start);
+ *out_package = StringPiece(start, current - start);
start = current + 1;
}
current++;
}
- out_entry->assign(start, end - start);
+ *out_entry = StringPiece(start, end - start);
return !(has_package_separator && out_package->empty()) &&
!(has_type_separator && out_type->empty());
@@ -50,7 +50,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
- const StringPiece& package_name) {
+ StringPiece package_name) {
AssetManager2::ResourceName name{
.package = package_name.data(),
.package_len = package_name.size(),
diff --git a/libs/androidfw/StringPool.cpp b/libs/androidfw/StringPool.cpp
new file mode 100644
index 000000000000..1cb8df311c89
--- /dev/null
+++ b/libs/androidfw/StringPool.cpp
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2015 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 <androidfw/BigBuffer.h>
+#include <androidfw/StringPool.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+
+using ::android::StringPiece;
+
+namespace android {
+
+StringPool::Ref::Ref() : entry_(nullptr) {
+}
+
+StringPool::Ref::Ref(const StringPool::Ref& rhs) : entry_(rhs.entry_) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
+}
+
+StringPool::Ref::Ref(StringPool::Entry* entry) : entry_(entry) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
+}
+
+StringPool::Ref::~Ref() {
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+}
+
+StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
+ if (rhs.entry_ != nullptr) {
+ rhs.entry_->ref_++;
+ }
+
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+ entry_ = rhs.entry_;
+ return *this;
+}
+
+bool StringPool::Ref::operator==(const Ref& rhs) const {
+ return entry_->value == rhs.entry_->value;
+}
+
+bool StringPool::Ref::operator!=(const Ref& rhs) const {
+ return entry_->value != rhs.entry_->value;
+}
+
+const std::string* StringPool::Ref::operator->() const {
+ return &entry_->value;
+}
+
+const std::string& StringPool::Ref::operator*() const {
+ return entry_->value;
+}
+
+size_t StringPool::Ref::index() const {
+ // Account for the styles, which *always* come first.
+ return entry_->pool_->styles_.size() + entry_->index_;
+}
+
+const StringPool::Context& StringPool::Ref::GetContext() const {
+ return entry_->context;
+}
+
+StringPool::StyleRef::StyleRef() : entry_(nullptr) {
+}
+
+StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : entry_(rhs.entry_) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
+}
+
+StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : entry_(entry) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
+}
+
+StringPool::StyleRef::~StyleRef() {
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+}
+
+StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
+ if (rhs.entry_ != nullptr) {
+ rhs.entry_->ref_++;
+ }
+
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+ entry_ = rhs.entry_;
+ return *this;
+}
+
+bool StringPool::StyleRef::operator==(const StyleRef& rhs) const {
+ if (entry_->value != rhs.entry_->value) {
+ return false;
+ }
+
+ if (entry_->spans.size() != rhs.entry_->spans.size()) {
+ return false;
+ }
+
+ auto rhs_iter = rhs.entry_->spans.begin();
+ for (const Span& span : entry_->spans) {
+ const Span& rhs_span = *rhs_iter;
+ if (span.first_char != rhs_span.first_char || span.last_char != rhs_span.last_char ||
+ span.name != rhs_span.name) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const {
+ return !operator==(rhs);
+}
+
+const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
+ return entry_;
+}
+
+const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
+ return *entry_;
+}
+
+size_t StringPool::StyleRef::index() const {
+ return entry_->index_;
+}
+
+const StringPool::Context& StringPool::StyleRef::GetContext() const {
+ return entry_->context;
+}
+
+StringPool::Ref StringPool::MakeRef(StringPiece str) {
+ return MakeRefImpl(str, Context{}, true);
+}
+
+StringPool::Ref StringPool::MakeRef(StringPiece str, const Context& context) {
+ return MakeRefImpl(str, context, true);
+}
+
+StringPool::Ref StringPool::MakeRefImpl(StringPiece str, const Context& context, bool unique) {
+ if (unique) {
+ auto range = indexed_strings_.equal_range(str);
+ for (auto iter = range.first; iter != range.second; ++iter) {
+ if (context.priority == iter->second->context.priority) {
+ return Ref(iter->second);
+ }
+ }
+ }
+
+ std::unique_ptr<Entry> entry(new Entry());
+ entry->value = std::string(str);
+ entry->context = context;
+ entry->index_ = strings_.size();
+ entry->ref_ = 0;
+ entry->pool_ = this;
+
+ Entry* borrow = entry.get();
+ strings_.emplace_back(std::move(entry));
+ indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
+ return Ref(borrow);
+}
+
+StringPool::Ref StringPool::MakeRef(const Ref& ref) {
+ if (ref.entry_->pool_ == this) {
+ return ref;
+ }
+ return MakeRef(ref.entry_->value, ref.entry_->context);
+}
+
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str) {
+ return MakeRef(str, Context{});
+}
+
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str, const Context& context) {
+ std::unique_ptr<StyleEntry> entry(new StyleEntry());
+ entry->value = str.str;
+ entry->context = context;
+ entry->index_ = styles_.size();
+ entry->ref_ = 0;
+ for (const android::Span& span : str.spans) {
+ entry->spans.emplace_back(Span{MakeRef(span.name), span.first_char, span.last_char});
+ }
+
+ StyleEntry* borrow = entry.get();
+ styles_.emplace_back(std::move(entry));
+ return StyleRef(borrow);
+}
+
+StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) {
+ std::unique_ptr<StyleEntry> entry(new StyleEntry());
+ entry->value = ref.entry_->value;
+ entry->context = ref.entry_->context;
+ entry->index_ = styles_.size();
+ entry->ref_ = 0;
+ for (const Span& span : ref.entry_->spans) {
+ entry->spans.emplace_back(Span{MakeRef(*span.name), span.first_char, span.last_char});
+ }
+
+ StyleEntry* borrow = entry.get();
+ styles_.emplace_back(std::move(entry));
+ return StyleRef(borrow);
+}
+
+void StringPool::ReAssignIndices() {
+ // Assign the style indices.
+ const size_t style_len = styles_.size();
+ for (size_t index = 0; index < style_len; index++) {
+ styles_[index]->index_ = index;
+ }
+
+ // Assign the string indices.
+ const size_t string_len = strings_.size();
+ for (size_t index = 0; index < string_len; index++) {
+ strings_[index]->index_ = index;
+ }
+}
+
+void StringPool::Merge(StringPool&& pool) {
+ // First, change the owning pool for the incoming strings.
+ for (std::unique_ptr<Entry>& entry : pool.strings_) {
+ entry->pool_ = this;
+ }
+
+ // Now move the styles, strings, and indices over.
+ std::move(pool.styles_.begin(), pool.styles_.end(), std::back_inserter(styles_));
+ pool.styles_.clear();
+ std::move(pool.strings_.begin(), pool.strings_.end(), std::back_inserter(strings_));
+ pool.strings_.clear();
+ indexed_strings_.insert(pool.indexed_strings_.begin(), pool.indexed_strings_.end());
+ pool.indexed_strings_.clear();
+
+ ReAssignIndices();
+}
+
+void StringPool::HintWillAdd(size_t string_count, size_t style_count) {
+ strings_.reserve(strings_.size() + string_count);
+ styles_.reserve(styles_.size() + style_count);
+}
+
+void StringPool::Prune() {
+ const auto iter_end = indexed_strings_.end();
+ auto index_iter = indexed_strings_.begin();
+ while (index_iter != iter_end) {
+ if (index_iter->second->ref_ <= 0) {
+ index_iter = indexed_strings_.erase(index_iter);
+ } else {
+ ++index_iter;
+ }
+ }
+
+ auto end_iter2 =
+ std::remove_if(strings_.begin(), strings_.end(),
+ [](const std::unique_ptr<Entry>& entry) -> bool { return entry->ref_ <= 0; });
+ auto end_iter3 = std::remove_if(
+ styles_.begin(), styles_.end(),
+ [](const std::unique_ptr<StyleEntry>& entry) -> bool { return entry->ref_ <= 0; });
+
+ // Remove the entries at the end or else we'll be accessing a deleted string from the StyleEntry.
+ strings_.erase(end_iter2, strings_.end());
+ styles_.erase(end_iter3, styles_.end());
+
+ ReAssignIndices();
+}
+
+template <typename E>
+static void SortEntries(
+ std::vector<std::unique_ptr<E>>& entries,
+ const std::function<int(const StringPool::Context&, const StringPool::Context&)>& cmp) {
+ using UEntry = std::unique_ptr<E>;
+
+ if (cmp != nullptr) {
+ std::sort(entries.begin(), entries.end(), [&cmp](const UEntry& a, const UEntry& b) -> bool {
+ int r = cmp(a->context, b->context);
+ if (r == 0) {
+ r = a->value.compare(b->value);
+ }
+ return r < 0;
+ });
+ } else {
+ std::sort(entries.begin(), entries.end(),
+ [](const UEntry& a, const UEntry& b) -> bool { return a->value < b->value; });
+ }
+}
+
+void StringPool::Sort(const std::function<int(const Context&, const Context&)>& cmp) {
+ SortEntries(styles_, cmp);
+ SortEntries(strings_, cmp);
+ ReAssignIndices();
+}
+
+template <typename T>
+static T* EncodeLength(T* data, size_t length) {
+ static_assert(std::is_integral<T>::value, "wat.");
+
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ if (length > kMaxSize) {
+ *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
+ }
+ *data++ = length;
+ return data;
+}
+
+/**
+ * Returns the maximum possible string length that can be successfully encoded
+ * using 2 units of the specified T.
+ * EncodeLengthMax<char> -> maximum unit length of 0x7FFF
+ * EncodeLengthMax<char16_t> -> maximum unit length of 0x7FFFFFFF
+ **/
+template <typename T>
+static size_t EncodeLengthMax() {
+ static_assert(std::is_integral<T>::value, "wat.");
+
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8 * 2) - 1);
+ constexpr size_t max = kMask - 1;
+ return max;
+}
+
+/**
+ * Returns the number of units (1 or 2) needed to encode the string length
+ * before writing the string.
+ */
+template <typename T>
+static size_t EncodedLengthUnits(size_t length) {
+ static_assert(std::is_integral<T>::value, "wat.");
+
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ return length > kMaxSize ? 2 : 1;
+}
+
+const std::string kStringTooLarge = "STRING_TOO_LARGE";
+
+static bool EncodeString(const std::string& str, const bool utf8, BigBuffer* out,
+ IDiagnostics* diag) {
+ if (utf8) {
+ const std::string& encoded = util::Utf8ToModifiedUtf8(str);
+ const ssize_t utf16_length =
+ utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(encoded.data()), encoded.size());
+ CHECK(utf16_length >= 0);
+
+ // Make sure the lengths to be encoded do not exceed the maximum length that
+ // can be encoded using chars
+ if ((((size_t)encoded.size()) > EncodeLengthMax<char>()) ||
+ (((size_t)utf16_length) > EncodeLengthMax<char>())) {
+ diag->Error(DiagMessage() << "string too large to encode using UTF-8 "
+ << "written instead as '" << kStringTooLarge << "'");
+
+ EncodeString(kStringTooLarge, utf8, out, diag);
+ return false;
+ }
+
+ const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
+ EncodedLengthUnits<char>(encoded.size()) + encoded.size() + 1;
+
+ char* data = out->NextBlock<char>(total_size);
+
+ // First encode the UTF16 string length.
+ data = EncodeLength(data, utf16_length);
+
+ // Now encode the size of the real UTF8 string.
+ data = EncodeLength(data, encoded.size());
+ strncpy(data, encoded.data(), encoded.size());
+
+ } else {
+ const std::u16string encoded = util::Utf8ToUtf16(str);
+ const ssize_t utf16_length = encoded.size();
+
+ // Make sure the length to be encoded does not exceed the maximum possible
+ // length that can be encoded
+ if (((size_t)utf16_length) > EncodeLengthMax<char16_t>()) {
+ diag->Error(DiagMessage() << "string too large to encode using UTF-16 "
+ << "written instead as '" << kStringTooLarge << "'");
+
+ EncodeString(kStringTooLarge, utf8, out, diag);
+ return false;
+ }
+
+ // Total number of 16-bit words to write.
+ const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
+
+ char16_t* data = out->NextBlock<char16_t>(total_size);
+
+ // Encode the actual UTF16 string length.
+ data = EncodeLength(data, utf16_length);
+ const size_t byte_length = encoded.size() * sizeof(char16_t);
+
+ // NOTE: For some reason, strncpy16(data, entry->value.data(),
+ // entry->value.size()) truncates the string.
+ memcpy(data, encoded.data(), byte_length);
+
+ // The null-terminating character is already here due to the block of data
+ // being set to 0s on allocation.
+ }
+
+ return true;
+}
+
+bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag) {
+ bool no_error = true;
+ const size_t start_index = out->size();
+ android::ResStringPool_header* header = out->NextBlock<android::ResStringPool_header>();
+ header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE);
+ header->header.headerSize = util::HostToDevice16(sizeof(*header));
+ header->stringCount = util::HostToDevice32(pool.size());
+ header->styleCount = util::HostToDevice32(pool.styles_.size());
+ if (utf8) {
+ header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ }
+
+ uint32_t* indices = pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+ uint32_t* style_indices =
+ pool.styles_.size() != 0 ? out->NextBlock<uint32_t>(pool.styles_.size()) : nullptr;
+
+ const size_t before_strings_index = out->size();
+ header->stringsStart = before_strings_index - start_index;
+
+ // Styles always come first.
+ for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+ *indices++ = out->size() - before_strings_index;
+ no_error = EncodeString(entry->value, utf8, out, diag) && no_error;
+ }
+
+ for (const std::unique_ptr<Entry>& entry : pool.strings_) {
+ *indices++ = out->size() - before_strings_index;
+ no_error = EncodeString(entry->value, utf8, out, diag) && no_error;
+ }
+
+ out->Align4();
+
+ if (style_indices != nullptr) {
+ const size_t before_styles_index = out->size();
+ header->stylesStart = util::HostToDevice32(before_styles_index - start_index);
+
+ for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+ *style_indices++ = out->size() - before_styles_index;
+
+ if (!entry->spans.empty()) {
+ android::ResStringPool_span* span =
+ out->NextBlock<android::ResStringPool_span>(entry->spans.size());
+ for (const Span& s : entry->spans) {
+ span->name.index = util::HostToDevice32(s.name.index());
+ span->firstChar = util::HostToDevice32(s.first_char);
+ span->lastChar = util::HostToDevice32(s.last_char);
+ span++;
+ }
+ }
+
+ uint32_t* spanEnd = out->NextBlock<uint32_t>();
+ *spanEnd = android::ResStringPool_span::END;
+ }
+
+ // The error checking code in the platform looks for an entire
+ // ResStringPool_span structure worth of 0xFFFFFFFF at the end
+ // of the style block, so fill in the remaining 2 32bit words
+ // with 0xFFFFFFFF.
+ const size_t padding_length =
+ sizeof(android::ResStringPool_span) - sizeof(android::ResStringPool_span::name);
+ uint8_t* padding = out->NextBlock<uint8_t>(padding_length);
+ memset(padding, 0xff, padding_length);
+ out->Align4();
+ }
+ header->header.size = util::HostToDevice32(out->size() - start_index);
+ return no_error;
+}
+
+bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) {
+ return Flatten(out, pool, true, diag);
+}
+
+bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) {
+ return Flatten(out, pool, false, diag);
+}
+
+} // namespace android
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
index 647aa197a94d..70d14a11830e 100644
--- a/libs/androidfw/TypeWrappers.cpp
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -59,7 +59,9 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
+ dtohl(type->header.size);
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
- if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
+ const size_t indexSize = type->flags & ResTable_type::FLAG_OFFSET16 ?
+ sizeof(uint16_t) : sizeof(uint32_t);
+ if (reinterpret_cast<uintptr_t>(entryIndices) + (indexSize * entryCount) > containerEnd) {
ALOGE("Type's entry indices extend beyond its boundaries");
return NULL;
}
@@ -73,6 +75,9 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
}
entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
+ } else if (type->flags & ResTable_type::FLAG_OFFSET16) {
+ auto entryIndices16 = reinterpret_cast<const uint16_t*>(entryIndices);
+ entryOffset = offset_from16(entryIndices16[mIndex]);
} else {
entryOffset = dtohl(entryIndices[mIndex]);
}
@@ -91,11 +96,11 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
return NULL;
- } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
+ } else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) {
ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
return NULL;
- } else if (dtohs(entry->size) < sizeof(*entry)) {
- ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
+ } else if (entry->size() < sizeof(*entry)) {
+ ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size());
return NULL;
}
return entry;
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
index 59c9d640bb91..be55fe8b4bb6 100644
--- a/libs/androidfw/Util.cpp
+++ b/libs/androidfw/Util.cpp
@@ -42,7 +42,7 @@ void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out
}
}
-std::u16string Utf8ToUtf16(const StringPiece& utf8) {
+std::u16string Utf8ToUtf16(StringPiece utf8) {
ssize_t utf16_length =
utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
if (utf16_length <= 0) {
@@ -56,7 +56,7 @@ std::u16string Utf8ToUtf16(const StringPiece& utf8) {
return utf16;
}
-std::string Utf16ToUtf8(const StringPiece16& utf16) {
+std::string Utf16ToUtf8(StringPiece16 utf16) {
ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
if (utf8_length <= 0) {
return {};
@@ -68,28 +68,151 @@ std::string Utf16ToUtf8(const StringPiece16& utf16) {
return utf8;
}
-static std::vector<std::string> SplitAndTransform(
- const StringPiece& str, char sep, const std::function<char(char)>& f) {
+std::string Utf8ToModifiedUtf8(std::string_view utf8) {
+ // Java uses Modified UTF-8 which only supports the 1, 2, and 3 byte formats of UTF-8. To encode
+ // 4 byte UTF-8 codepoints, Modified UTF-8 allows the use of surrogate pairs in the same format
+ // of CESU-8 surrogate pairs. Calculate the size of the utf8 string with all 4 byte UTF-8
+ // codepoints replaced with 2 3 byte surrogate pairs
+ size_t modified_size = 0;
+ const size_t size = utf8.size();
+ for (size_t i = 0; i < size; i++) {
+ if (((uint8_t)utf8[i] >> 4) == 0xF) {
+ modified_size += 6;
+ i += 3;
+ } else {
+ modified_size++;
+ }
+ }
+
+ // Early out if no 4 byte codepoints are found
+ if (size == modified_size) {
+ return std::string(utf8);
+ }
+
+ std::string output;
+ output.reserve(modified_size);
+ for (size_t i = 0; i < size; i++) {
+ if (((uint8_t)utf8[i] >> 4) == 0xF) {
+ int32_t codepoint = utf32_from_utf8_at(utf8.data(), size, i, nullptr);
+
+ // Calculate the high and low surrogates as UTF-16 would
+ int32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800;
+ int32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00;
+
+ // Encode each surrogate in UTF-8
+ output.push_back((char)(0xE4 | ((high >> 12) & 0xF)));
+ output.push_back((char)(0x80 | ((high >> 6) & 0x3F)));
+ output.push_back((char)(0x80 | (high & 0x3F)));
+ output.push_back((char)(0xE4 | ((low >> 12) & 0xF)));
+ output.push_back((char)(0x80 | ((low >> 6) & 0x3F)));
+ output.push_back((char)(0x80 | (low & 0x3F)));
+ i += 3;
+ } else {
+ output.push_back(utf8[i]);
+ }
+ }
+
+ return output;
+}
+
+std::string ModifiedUtf8ToUtf8(std::string_view modified_utf8) {
+ // The UTF-8 representation will have a byte length less than or equal to the Modified UTF-8
+ // representation.
+ std::string output;
+ output.reserve(modified_utf8.size());
+
+ size_t index = 0;
+ const size_t modified_size = modified_utf8.size();
+ while (index < modified_size) {
+ size_t next_index;
+ int32_t high_surrogate =
+ utf32_from_utf8_at(modified_utf8.data(), modified_size, index, &next_index);
+ if (high_surrogate < 0) {
+ return {};
+ }
+
+ // Check that the first codepoint is within the high surrogate range
+ if (high_surrogate >= 0xD800 && high_surrogate <= 0xDB7F) {
+ int32_t low_surrogate =
+ utf32_from_utf8_at(modified_utf8.data(), modified_size, next_index, &next_index);
+ if (low_surrogate < 0) {
+ return {};
+ }
+
+ // Check that the second codepoint is within the low surrogate range
+ if (low_surrogate >= 0xDC00 && low_surrogate <= 0xDFFF) {
+ const char32_t codepoint =
+ (char32_t)(((high_surrogate - 0xD800) * 0x400) + (low_surrogate - 0xDC00) + 0x10000);
+
+ // The decoded codepoint should represent a 4 byte, UTF-8 character
+ const size_t utf8_length = (size_t)utf32_to_utf8_length(&codepoint, 1);
+ if (utf8_length != 4) {
+ return {};
+ }
+
+ // Encode the UTF-8 representation of the codepoint into the string
+ const size_t start_index = output.size();
+ output.resize(start_index + utf8_length);
+ char* start = &output[start_index];
+ utf32_to_utf8((char32_t*)&codepoint, 1, start, utf8_length + 1);
+
+ index = next_index;
+ continue;
+ }
+ }
+
+ // Append non-surrogate pairs to the output string
+ for (size_t i = index; i < next_index; i++) {
+ output.push_back(modified_utf8[i]);
+ }
+ index = next_index;
+ }
+ return output;
+}
+
+template <class Func>
+static std::vector<std::string> SplitAndTransform(StringPiece str, char sep, Func&& f) {
std::vector<std::string> parts;
const StringPiece::const_iterator end = std::end(str);
StringPiece::const_iterator start = std::begin(str);
StringPiece::const_iterator current;
do {
current = std::find(start, end, sep);
- parts.emplace_back(str.substr(start, current).to_string());
- if (f) {
- std::string& part = parts.back();
- std::transform(part.begin(), part.end(), part.begin(), f);
- }
+ parts.emplace_back(StringPiece(start, current - start));
+ std::string& part = parts.back();
+ std::transform(part.begin(), part.end(), part.begin(), f);
start = current + 1;
} while (current != end);
return parts;
}
-std::vector<std::string> SplitAndLowercase(const StringPiece& str, char sep) {
- return SplitAndTransform(str, sep, ::tolower);
+std::vector<std::string> SplitAndLowercase(StringPiece str, char sep) {
+ return SplitAndTransform(str, sep, [](char c) { return ::tolower(c); });
}
+std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer) {
+ auto data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
+ uint8_t* p = data.get();
+ for (const auto& block : buffer) {
+ memcpy(p, block.buffer.get(), block.size);
+ p += block.size;
+ }
+ return data;
+}
+
+StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
+ if (auto str = pool.stringAt(idx); str.ok()) {
+ return *str;
+ }
+ return StringPiece16();
+}
+
+std::string GetString(const android::ResStringPool& pool, size_t idx) {
+ if (auto str = pool.string8At(idx); str.ok()) {
+ return ModifiedUtf8ToUtf8(*str);
+ }
+ return Utf16ToUtf8(GetString16(pool, idx));
+}
} // namespace util
} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 1bde792da2ba..f10cb9bf480a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -17,6 +17,7 @@
#ifndef ANDROIDFW_ASSETMANAGER2_H_
#define ANDROIDFW_ASSETMANAGER2_H_
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include <array>
@@ -104,7 +105,7 @@ class AssetManager2 {
// new resource IDs.
bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
- inline const std::vector<const ApkAssets*> GetApkAssets() const {
+ inline const std::vector<const ApkAssets*>& GetApkAssets() const {
return apk_assets_;
}
@@ -124,8 +125,7 @@ class AssetManager2 {
uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
// Returns a string representation of the overlayable API of a package.
- bool GetOverlayablesToString(const android::StringPiece& package_name,
- std::string* out) const;
+ bool GetOverlayablesToString(android::StringPiece package_name, std::string* out) const;
const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(
uint32_t package_id) const;
@@ -321,17 +321,8 @@ class AssetManager2 {
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
- package_property_t excluded_property_flags = 0U) const {
- for (const PackageGroup& package_group : package_groups_) {
- const auto loaded_package = package_group.packages_.front().loaded_package_;
- if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
- && !func(loaded_package->GetPackageName(),
- package_group.dynamic_ref_table->mAssignedPackageId)) {
- return;
- }
- }
- }
+ void ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const;
void DumpToLog() const;
@@ -572,6 +563,7 @@ class Theme {
AssetManager2* asset_manager_ = nullptr;
uint32_t type_spec_flags_ = 0u;
+ std::vector<uint32_t> keys_;
std::vector<Entry> entries_;
};
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 966ec74c1786..d33c325ff369 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -46,7 +47,7 @@ struct AssetsProvider {
// Iterate over all files and directories provided by the interface. The order of iteration is
// stable.
virtual bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+ base::function_ref<void(StringPiece, FileType)> f) const = 0;
// Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an
// APk, a directory, or some other file type.
@@ -80,8 +81,8 @@ struct AssetsProvider {
// Supplies assets from a zip archive.
struct ZipAssetsProvider : public AssetsProvider {
- static std::unique_ptr<ZipAssetsProvider> Create(std::string path,
- package_property_t flags);
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path, package_property_t flags,
+ base::unique_fd fd = {});
static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
std::string friendly_name,
@@ -90,7 +91,7 @@ struct ZipAssetsProvider : public AssetsProvider {
off64_t len = kUnknownLength);
bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -108,7 +109,12 @@ struct ZipAssetsProvider : public AssetsProvider {
time_t last_mod_time);
struct PathOrDebugName {
- PathOrDebugName(std::string&& value, bool is_path);
+ static PathOrDebugName Path(std::string value) {
+ return {std::move(value), true};
+ }
+ static PathOrDebugName DebugName(std::string value) {
+ return {std::move(value), false};
+ }
// Retrieves the path or null if this class represents a debug name.
WARN_UNUSED const std::string* GetPath() const;
@@ -117,11 +123,16 @@ struct ZipAssetsProvider : public AssetsProvider {
WARN_UNUSED const std::string& GetDebugName() const;
private:
+ PathOrDebugName(std::string value, bool is_path) : value_(std::move(value)), is_path_(is_path) {
+ }
std::string value_;
bool is_path_;
};
- std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ struct ZipCloser {
+ void operator()(ZipArchive* a) const;
+ };
+ std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
PathOrDebugName name_;
package_property_t flags_;
time_t last_mod_time_;
@@ -132,7 +143,7 @@ struct DirectoryAssetsProvider : public AssetsProvider {
static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -157,7 +168,7 @@ struct MultiAssetsProvider : public AssetsProvider {
std::unique_ptr<AssetsProvider>&& secondary);
bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -181,10 +192,10 @@ struct MultiAssetsProvider : public AssetsProvider {
// Does not provide any assets.
struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create();
- static std::unique_ptr<AssetsProvider> Create(const std::string& path);
+ static std::unique_ptr<AssetsProvider> Create(std::string path);
bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
diff --git a/libs/androidfw/include/androidfw/BigBuffer.h b/libs/androidfw/include/androidfw/BigBuffer.h
new file mode 100644
index 000000000000..b99a4edf9d88
--- /dev/null
+++ b/libs/androidfw/include/androidfw/BigBuffer.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_BIG_BUFFER_H
+#define _ANDROID_BIG_BUFFER_H
+
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+namespace android {
+
+/**
+ * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
+ * in which to write without knowing the full size of the entire payload.
+ * This is essentially a list of memory blocks. As one fills up, another
+ * block is allocated and appended to the end of the list.
+ */
+class BigBuffer {
+ public:
+ /**
+ * A contiguous block of allocated memory.
+ */
+ struct Block {
+ /**
+ * Pointer to the memory.
+ */
+ std::unique_ptr<uint8_t[]> buffer;
+
+ /**
+ * Size of memory that is currently occupied. The actual
+ * allocation may be larger.
+ */
+ size_t size;
+
+ private:
+ friend class BigBuffer;
+
+ /**
+ * The size of the memory block allocation.
+ */
+ size_t block_size_;
+ };
+
+ typedef std::vector<Block>::const_iterator const_iterator;
+
+ /**
+ * Create a BigBuffer with block allocation sizes
+ * of block_size.
+ */
+ explicit BigBuffer(size_t block_size);
+
+ BigBuffer(BigBuffer&& rhs) noexcept;
+
+ /**
+ * Number of occupied bytes in all the allocated blocks.
+ */
+ size_t size() const;
+
+ /**
+ * Returns a pointer to an array of T, where T is
+ * a POD type. The elements are zero-initialized.
+ */
+ template <typename T>
+ T* NextBlock(size_t count = 1);
+
+ /**
+ * Returns the next block available and puts the size in out_count.
+ * This is useful for grabbing blocks where the size doesn't matter.
+ * Use BackUp() to give back any bytes that were not used.
+ */
+ void* NextBlock(size_t* out_count);
+
+ /**
+ * Backs up count bytes. This must only be called after NextBlock()
+ * and can not be larger than sizeof(T) * count of the last NextBlock()
+ * call.
+ */
+ void BackUp(size_t count);
+
+ /**
+ * Moves the specified BigBuffer into this one. When this method
+ * returns, buffer is empty.
+ */
+ void AppendBuffer(BigBuffer&& buffer);
+
+ /**
+ * Pads the block with 'bytes' bytes of zero values.
+ */
+ void Pad(size_t bytes);
+
+ /**
+ * Pads the block so that it aligns on a 4 byte boundary.
+ */
+ void Align4();
+
+ size_t block_size() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ std::string to_string() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BigBuffer);
+
+ /**
+ * Returns a pointer to a buffer of the requested size.
+ * The buffer is zero-initialized.
+ */
+ void* NextBlockImpl(size_t size);
+
+ size_t block_size_;
+ size_t size_;
+ std::vector<Block> blocks_;
+};
+
+inline BigBuffer::BigBuffer(size_t block_size) : block_size_(block_size), size_(0) {
+}
+
+inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept
+ : block_size_(rhs.block_size_), size_(rhs.size_), blocks_(std::move(rhs.blocks_)) {
+}
+
+inline size_t BigBuffer::size() const {
+ return size_;
+}
+
+inline size_t BigBuffer::block_size() const {
+ return block_size_;
+}
+
+template <typename T>
+inline T* BigBuffer::NextBlock(size_t count) {
+ static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
+ CHECK(count != 0);
+ return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
+}
+
+inline void BigBuffer::BackUp(size_t count) {
+ Block& block = blocks_.back();
+ block.size -= count;
+ size_ -= count;
+}
+
+inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
+ std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
+ size_ += buffer.size_;
+ buffer.blocks_.clear();
+ buffer.size_ = 0;
+}
+
+inline void BigBuffer::Pad(size_t bytes) {
+ NextBlock<char>(bytes);
+}
+
+inline void BigBuffer::Align4() {
+ const size_t unaligned = size_ % 4;
+ if (unaligned != 0) {
+ Pad(4 - unaligned);
+ }
+}
+
+inline BigBuffer::const_iterator BigBuffer::begin() const {
+ return blocks_.begin();
+}
+
+inline BigBuffer::const_iterator BigBuffer::end() const {
+ return blocks_.end();
+}
+
+} // namespace android
+
+#endif // _ANDROID_BIG_BUFFER_H
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 949c9445b3e8..ca0a9eda9caa 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,6 +17,7 @@
#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H
+#include <algorithm>
#include <cstdint>
#include <cstring>
@@ -31,14 +32,16 @@ namespace android {
template <typename T>
class ByteBucketArray {
public:
- ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); }
+ ByteBucketArray() {
+ memset(buckets_, 0, sizeof(buckets_));
+ }
~ByteBucketArray() {
- for (size_t i = 0; i < kNumBuckets; i++) {
- if (buckets_[i] != NULL) {
- delete[] buckets_[i];
- }
- }
+ deleteBuckets();
+ }
+
+ void clear() {
+ deleteBuckets();
memset(buckets_, 0, sizeof(buckets_));
}
@@ -53,7 +56,7 @@ class ByteBucketArray {
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
+ if (bucket == nullptr) {
return default_;
}
return bucket[0x0f & static_cast<uint8_t>(index)];
@@ -64,9 +67,9 @@ class ByteBucketArray {
<< ") with size=" << size();
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
- T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
- bucket = buckets_[bucket_index] = new T[kBucketSize]();
+ T*& bucket = buckets_[bucket_index];
+ if (bucket == nullptr) {
+ bucket = new T[kBucketSize]();
}
return bucket[0x0f & static_cast<uint8_t>(index)];
}
@@ -80,11 +83,44 @@ class ByteBucketArray {
return true;
}
+ template <class Func>
+ void forEachItem(Func f) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ for (size_t j = 0; j < kBucketSize; j++) {
+ f((i << 4) | j, bucket[j]);
+ }
+ }
+ }
+ }
+
+ template <class Func>
+ void trimBuckets(Func isEmptyFunc) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ if (std::all_of(bucket, bucket + kBucketSize, isEmptyFunc)) {
+ delete[] bucket;
+ buckets_[i] = nullptr;
+ }
+ }
+ }
+ }
+
private:
enum { kNumBuckets = 16, kBucketSize = 16 };
+ void deleteBuckets() {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ if (buckets_[i] != nullptr) {
+ delete[] buckets_[i];
+ }
+ }
+ }
+
T* buckets_[kNumBuckets];
- T default_;
+ static inline const T default_ = {};
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 61d10cd4e55b..71087cdfb6fa 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -72,7 +72,7 @@ struct ConfigDescription : public ResTable_config {
* The resulting configuration has the appropriate sdkVersion defined
* for backwards compatibility.
*/
- static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);
+ static bool Parse(android::StringPiece str, ConfigDescription* out = nullptr);
/**
* If the configuration uses an axis that was added after
diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h
new file mode 100644
index 000000000000..4d5844eaa069
--- /dev/null
+++ b/libs/androidfw/include/androidfw/IDiagnostics.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_DIAGNOSTICS_H
+#define _ANDROID_DIAGNOSTICS_H
+
+#include <sstream>
+#include <string>
+
+#include "Source.h"
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
+namespace android {
+
+struct DiagMessageActual {
+ Source source;
+ std::string message;
+};
+
+struct DiagMessage {
+ public:
+ DiagMessage() = default;
+
+ explicit DiagMessage(android::StringPiece src) : source_(src) {
+ }
+
+ explicit DiagMessage(const Source& src) : source_(src) {
+ }
+
+ explicit DiagMessage(size_t line) : source_(Source().WithLine(line)) {
+ }
+
+ template <typename T>
+ DiagMessage& operator<<(const T& value) {
+ message_ << value;
+ return *this;
+ }
+
+ DiagMessageActual Build() const {
+ return DiagMessageActual{source_, message_.str()};
+ }
+
+ private:
+ Source source_;
+ std::stringstream message_;
+};
+
+template <>
+inline DiagMessage& DiagMessage::operator<<(const ::std::u16string& value) {
+ message_ << value;
+ return *this;
+}
+
+struct IDiagnostics {
+ virtual ~IDiagnostics() = default;
+
+ enum class Level { Note, Warn, Error };
+
+ virtual void Log(Level level, DiagMessageActual& actualMsg) = 0;
+
+ virtual void Error(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Error, actual);
+ }
+
+ virtual void Warn(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Warn, actual);
+ }
+
+ virtual void Note(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Note, actual);
+ }
+};
+
+class SourcePathDiagnostics : public IDiagnostics {
+ public:
+ SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : source_(src), diag_(diag) {
+ }
+
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ actual_msg.source.path = source_.path;
+ diag_->Log(level, actual_msg);
+ if (level == Level::Error) {
+ error = true;
+ }
+ }
+
+ bool HadError() {
+ return error;
+ }
+
+ private:
+ Source source_;
+ IDiagnostics* diag_;
+ bool error = false;
+
+ DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
+};
+
+class NoOpDiagnostics : public IDiagnostics {
+ public:
+ NoOpDiagnostics() = default;
+
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ (void)level;
+ (void)actual_msg;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(NoOpDiagnostics);
+};
+
+} // namespace android
+
+#endif /* _ANDROID_DIAGNOSTICS_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 6804472b3d17..60689128dffb 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -23,6 +23,7 @@
#include <variant>
#include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
#include "utils/ByteOrder.h"
@@ -35,6 +36,7 @@ struct Idmap_header;
struct Idmap_data_header;
struct Idmap_target_entry;
struct Idmap_target_entry_inline;
+struct Idmap_target_entry_inline_value;
struct Idmap_overlay_entry;
// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
@@ -91,7 +93,8 @@ class IdmapResMap {
public:
Result() = default;
explicit Result(uint32_t value) : data_(value) {};
- explicit Result(const Res_value& value) : data_(value) { };
+ explicit Result(std::map<ConfigDescription, Res_value> value) : data_(std::move(value)) {
+ }
// Returns `true` if the resource is overlaid.
explicit operator bool() const {
@@ -107,15 +110,16 @@ class IdmapResMap {
}
bool IsInlineValue() const {
- return std::get_if<Res_value>(&data_) != nullptr;
+ return std::get_if<2>(&data_) != nullptr;
}
- const Res_value& GetInlineValue() const {
- return std::get<Res_value>(data_);
+ const std::map<ConfigDescription, Res_value>& GetInlineValue() const {
+ return std::get<2>(data_);
}
private:
- std::variant<std::monostate, uint32_t, Res_value> data_;
+ std::variant<std::monostate, uint32_t,
+ std::map<ConfigDescription, Res_value> > data_;
};
// Looks up the value that overlays the target resource id.
@@ -129,12 +133,16 @@ class IdmapResMap {
explicit IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table);
const Idmap_data_header* data_header_;
const Idmap_target_entry* entries_;
const Idmap_target_entry_inline* inline_entries_;
+ const Idmap_target_entry_inline_value* inline_entry_values_;
+ const ConfigDescription* configurations_;
const uint8_t target_assigned_package_id_;
const OverlayDynamicRefTable* overlay_ref_table_;
@@ -149,8 +157,7 @@ class IdmapResMap {
class LoadedIdmap {
public:
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
- static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data);
+ static std::unique_ptr<LoadedIdmap> Load(StringPiece idmap_path, StringPiece idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {
@@ -170,8 +177,8 @@ class LoadedIdmap {
// Returns a mapping from target resource ids to overlay values.
const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table) const {
- return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
- target_assigned_package_id, overlay_ref_table);
+ return IdmapResMap(data_header_, target_entries_, target_inline_entries_, inline_entry_values_,
+ configurations_, target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
@@ -191,6 +198,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header_;
const Idmap_target_entry* target_entries_;
const Idmap_target_entry_inline* target_inline_entries_;
+ const Idmap_target_entry_inline_value* inline_entry_values_;
+ const ConfigDescription* configurations_;
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
@@ -207,6 +216,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values_,
+ const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path,
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index b3d6a4dcb955..4d12885ad291 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -99,8 +99,8 @@ enum : package_property_t {
};
struct OverlayableInfo {
- std::string name;
- std::string actor;
+ std::string_view name;
+ std::string_view actor;
uint32_t policy_flags;
};
@@ -166,14 +166,14 @@ class LoadedPackage {
base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
const std::u16string& entry_name) const;
- static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
- incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
+ static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+ GetEntry(incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
- static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
- incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
+ static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError>
+ GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
const ResStringPool* GetTypeStringPool() const {
@@ -275,7 +275,7 @@ class LoadedPackage {
return overlayable_map_;
}
- const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ const std::vector<std::pair<uint32_t, uint32_t>>& GetAliasResourceIdMap() const {
return alias_id_map_;
}
@@ -295,8 +295,8 @@ class LoadedPackage {
std::unordered_map<uint8_t, TypeSpec> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
- std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
- std::map<uint32_t, uint32_t> alias_id_map_;
+ std::vector<std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::vector<std::pair<uint32_t, uint32_t>> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
@@ -314,6 +314,8 @@ class LoadedArsc {
const LoadedIdmap* loaded_idmap = nullptr,
package_property_t property_flags = 0U);
+ static std::unique_ptr<LoadedArsc> Load(const LoadedIdmap* loaded_idmap = nullptr);
+
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<LoadedArsc> CreateEmpty();
@@ -338,6 +340,7 @@ class LoadedArsc {
LoadedArsc() = default;
bool LoadTable(
const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
+ bool LoadStringPool(const LoadedIdmap* loaded_idmap);
std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
diff --git a/libs/androidfw/include/androidfw/Locale.h b/libs/androidfw/include/androidfw/Locale.h
index 484ed79a8efd..8934bed098fe 100644
--- a/libs/androidfw/include/androidfw/Locale.h
+++ b/libs/androidfw/include/androidfw/Locale.h
@@ -39,10 +39,10 @@ struct LocaleValue {
/**
* Initialize this LocaleValue from a config string.
*/
- bool InitFromFilterString(const android::StringPiece& config);
+ bool InitFromFilterString(android::StringPiece config);
// Initializes this LocaleValue from a BCP-47 locale tag.
- bool InitFromBcp47Tag(const android::StringPiece& bcp47tag);
+ bool InitFromBcp47Tag(android::StringPiece bcp47tag);
/**
* Initialize this LocaleValue from parts of a vector.
@@ -70,7 +70,7 @@ struct LocaleValue {
inline bool operator>(const LocaleValue& o) const;
private:
- bool InitFromBcp47TagImpl(const android::StringPiece& bcp47tag, const char separator);
+ bool InitFromBcp47TagImpl(android::StringPiece bcp47tag, const char separator);
void set_language(const char* language);
void set_region(const char* language);
diff --git a/libs/androidfw/include/androidfw/PosixUtils.h b/libs/androidfw/include/androidfw/PosixUtils.h
index bb2084740a44..c46e5e6b3fb5 100644
--- a/libs/androidfw/include/androidfw/PosixUtils.h
+++ b/libs/androidfw/include/androidfw/PosixUtils.h
@@ -25,12 +25,18 @@ struct ProcResult {
int status;
std::string stdout_str;
std::string stderr_str;
+
+ explicit ProcResult(int status) : status(status) {}
+ ProcResult(ProcResult&&) noexcept = default;
+ ProcResult& operator=(ProcResult&&) noexcept = default;
+
+ explicit operator bool() const { return status >= 0; }
};
-// Fork, exec and wait for an external process. Return nullptr if the process could not be launched,
-// otherwise a ProcResult containing the external process' exit status and captured stdout and
-// stderr.
-std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv);
+// Fork, exec and wait for an external process. Returns status < 0 if the process could not be
+// launched, otherwise a ProcResult containing the external process' exit status and captured
+// stdout and stderr.
+ProcResult ExecuteBinary(const std::vector<std::string>& argv);
} // namespace util
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ResourceTimer.h b/libs/androidfw/include/androidfw/ResourceTimer.h
new file mode 100644
index 000000000000..74613519a920
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ResourceTimer.h
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROIDFW_RESOURCETIMER_H_
+#define ANDROIDFW_RESOURCETIMER_H_
+
+#include <time.h>
+#include <atomic>
+#include <vector>
+
+#include <utils/Mutex.h>
+#include <android-base/macros.h>
+#include <androidfw/Util.h>
+
+namespace android {
+
+// ResourceTimer captures the duration of short functions. Durations are accumulated in registers
+// and statistics are pulled back to the Java layer as needed.
+// To monitor an API, first add it to the Counter enumeration. Then, inside the API, create an
+// instance of ResourceTimer with the appropriate enumeral. The corresponding counter will be
+// updated when the ResourceTimer destructor is called, normally at the end of the enclosing block.
+class ResourceTimer {
+ public:
+ enum class Counter {
+ GetResourceValue,
+ RetrieveAttributes,
+
+ LastCounter = RetrieveAttributes,
+ };
+ static const int counterSize = static_cast<int>(Counter::LastCounter) + 1;
+ static char const *toString(Counter);
+
+ // Start a timer for the specified counter.
+ ResourceTimer(Counter);
+ // The block is exiting. If the timer is active, record it.
+ ~ResourceTimer();
+ // This records the elapsed time and disables further recording. Use this if the containing
+ // block includes extra processing that should not be included in the timer. The method is
+ // destructive in that the timer is no longer valid and further calls to record() will be
+ // ignored.
+ void record();
+ // This cancels a timer. Elapsed time will neither be computed nor recorded.
+ void cancel();
+
+ // A single timer contains the count of events and the cumulative time spent handling the
+ // events. It also includes the smallest value seen and 10 largest values seen. Finally, it
+ // includes a histogram of values that approximates a semi-log.
+
+ // The timer can compute percentiles of recorded events. For example, the p50 value is a time
+ // such that 50% of the readings are below the value and 50% are above the value. The
+ // granularity in the readings means that a percentile cannot always be computed. In this case,
+ // the percentile is reported as zero. (The simplest example is when there is a single
+ // reading.) Even if the value can be computed, it will not be exact. Therefore, a percentile
+ // is actually reported as two values: the lowest time at which it might be valid and the
+ // highest time at which it might be valid.
+ struct Timer {
+ static const size_t MaxLargest = 5;
+
+ // The construct zeros all the fields. The destructor releases memory allocated to the
+ // buckets.
+ Timer();
+ ~Timer();
+
+ // The following summary values are set to zero on a reset. All times are in ns.
+
+ // The total number of events recorded.
+ int count;
+ // The total duration of events.
+ int64_t total;
+ // The smallest event duration seen. This is guaranteed to be non-zero if count is greater
+ // than 0.
+ int mintime;
+ // The largest event duration seen.
+ int maxtime;
+
+ // The largest values seen. Element 0 is the largest value seen (and is the same as maxtime,
+ // above). Element 1 is the next largest, and so on. If count is less than MaxLargest,
+ // unused elements will be zero.
+ int largest[MaxLargest];
+
+ // The p50 value is a time such that 50% of the readings are below that time and 50% of the
+ // readings.
+
+ // A single percentile is defined by the lowest value supported by the readings and the
+ // highest value supported by the readings.
+ struct Percentile {
+ // The nominal time (in ns) of the percentile. The true percentile is guaranteed to be less
+ // than or equal to this time.
+ int nominal;
+ // The actual percentile of the nominal time.
+ int nominal_actual;
+ // The time of the next lower bin. The true percentile is guaranteed to be greater than
+ // this time.
+ int floor;
+ // The actual percentile of the floor time.
+ int floor_actual;
+
+ // Fill in a percentile given the cumulative to the bin, the count in the current bin, the
+ // total count, the width of the bin, and the time of the bin.
+ void compute(int cumulative, int current, int count, int width, int time);
+ };
+
+ // The structure that holds the percentiles.
+ struct {
+ Percentile p50;
+ Percentile p90;
+ Percentile p95;
+ Percentile p99;
+ } pvalues;
+
+ // Set all counters to zero.
+ void reset();
+ // Record an event. The input time is in ns.
+ void record(int);
+ // Compute the percentiles. Percentiles are computed on demand, as the computation is too
+ // expensive to be done inline.
+ void compute();
+
+ // Copy one timer to another. If reset is true then the src is reset immediately after the
+ // copy. The reset flag is exploited to make the copy faster. Any data in dst is lost.
+ static void copy(Timer &dst, Timer &src, bool reset);
+
+ private:
+ // Free any buckets.
+ void freeBuckets();
+
+ // Readings are placed in bins, which are orgzanized into decades. The decade 0 covers
+ // [0,100) in steps of 1us. Decade 1 covers [0,1000) in steps of 10us. Decade 2 covers
+ // [0,10000) in steps of 100us. And so on.
+
+ // An event is placed in the first bin that can hold it. This means that events in the range
+ // of [0,100) are placed in the first decade, events in the range of [0,1000) are placed in
+ // the second decade, and so on. This also means that the first 10% of the bins are unused
+ // in each decade after the first.
+
+ // The design provides at least two significant digits across the range of [0,10000).
+
+ static const size_t MaxDimension = 4;
+ static const size_t MaxBuckets = 100;
+
+ // The range of each dimension. The lower value is always zero.
+ static const int range[MaxDimension];
+ // The width of each bin, by dimension
+ static const int width[MaxDimension];
+
+ // A histogram of the values seen. Centuries are allocated as needed, to minimize the memory
+ // impact.
+ int *buckets[MaxDimension];
+ };
+
+ // Fetch one Timer. The function has a short-circuit behavior: if the count is zero then
+ // destination count is set to zero and the function returns false. Otherwise, the destination
+ // is a copy of the source and the function returns true. This behavior lowers the cost of
+ // handling unused timers.
+ static bool copy(int src, Timer &dst, bool reset);
+
+ // Enable the timers. Timers are initially disabled. Enabling timers allocates memory for the
+ // counters. Timers cannot be disabled.
+ static void enable();
+
+ private:
+ // An internal reset method. This does not take a lock.
+ static void reset();
+
+ // Helper method to convert a counter into an enum. Presumably, this will be inlined into zero
+ // actual cpu instructions.
+ static inline std::vector<unsigned int>::size_type toIndex(Counter c) {
+ return static_cast<std::vector<unsigned int>::size_type>(c);
+ }
+
+ // Every counter has an associated lock. The lock has been factored into a separate class to
+ // keep the Timer class a POD.
+ struct GuardedTimer {
+ Mutex lock_;
+ Timer timer_;
+ };
+
+ // Scoped timer
+ struct ScopedTimer {
+ AutoMutex _l;
+ Timer &t;
+ ScopedTimer(GuardedTimer &g) :
+ _l(g.lock_), t(g.timer_) {
+ }
+ Timer *operator->() {
+ return &t;
+ }
+ Timer& operator*() {
+ return t;
+ }
+ };
+
+ // An individual timer is active (or not), is tracking a specific API, and has a start time.
+ // The api and the start time are undefined if the timer is not active.
+ bool active_;
+ Counter api_;
+ struct timespec start_;
+
+ // The global enable flag. This is initially false and may be set true by the java runtime.
+ static std::atomic<bool> enabled_;
+
+ // The global timers. The memory for the timers is not allocated until the timers are enabled.
+ static std::atomic<GuardedTimer *> counter_;
+};
+
+} // namespace android
+
+#endif /* ANDROIDFW_RESOURCETIMER_H_ */
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 3d66244646d5..52321dad8a5a 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -21,11 +21,13 @@
#define _LIBS_UTILS_RESOURCE_TYPES_H
#include <android-base/expected.h>
+#include <android-base/unique_fd.h>
#include <androidfw/Asset.h>
#include <androidfw/Errors.h>
#include <androidfw/LocaleData.h>
#include <androidfw/StringPiece.h>
+#include <utils/ByteOrder.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -45,7 +47,7 @@
namespace android {
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
+constexpr const uint32_t kIdmapCurrentVersion = 0x00000009u;
// This must never change.
constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
@@ -53,10 +55,12 @@ constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endi
// The version should only be changed when a backwards-incompatible change must be made to the
// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format
// to prevent losing fabricated overlay data.
-constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1;
+constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3;
// Returns whether or not the path represents a fabricated overlay.
bool IsFabricatedOverlay(const std::string& path);
+bool IsFabricatedOverlay(const char* path);
+bool IsFabricatedOverlay(android::base::borrowed_fd fd);
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1098,7 +1102,7 @@ struct ResTable_config
SDKVERSION_ANY = 0
};
- enum {
+ enum {
MINORVERSION_ANY = 0
};
@@ -1437,6 +1441,10 @@ struct ResTable_type
// Mark any types that use this with a v26 qualifier to prevent runtime issues on older
// platforms.
FLAG_SPARSE = 0x01,
+
+ // If set, the offsets to the entries are encoded in 16-bit, real_offset = offset * 4u
+ // An 16-bit offset of 0xffffu means a NO_ENTRY
+ FLAG_OFFSET16 = 0x02,
};
uint8_t flags;
@@ -1453,6 +1461,11 @@ struct ResTable_type
ResTable_config config;
};
+// Convert a 16-bit offset to 32-bit if FLAG_OFFSET16 is set
+static inline uint32_t offset_from16(uint16_t off16) {
+ return dtohs(off16) == 0xffffu ? ResTable_type::NO_ENTRY : dtohs(off16) * 4u;
+}
+
// The minimum size required to read any version of ResTable_type.
constexpr size_t kResTableTypeMinSize =
sizeof(ResTable_type) - sizeof(ResTable_config) + sizeof(ResTable_config::size);
@@ -1480,6 +1493,8 @@ union ResTable_sparseTypeEntry {
static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t),
"ResTable_sparseTypeEntry must be 4 bytes in size");
+struct ResTable_map_entry;
+
/**
* This is the beginning of information about an entry in the resource
* table. It holds the reference to the name of this entry, and is
@@ -1487,12 +1502,11 @@ static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t),
* * A Res_value structure, if FLAG_COMPLEX is -not- set.
* * An array of ResTable_map structures, if FLAG_COMPLEX is set.
* These supply a set of name/value mappings of data.
+ * * If FLAG_COMPACT is set, this entry is a compact entry for
+ * simple values only
*/
-struct ResTable_entry
+union ResTable_entry
{
- // Number of bytes in this structure.
- uint16_t size;
-
enum {
// If set, this is a complex entry, holding a set of name/value
// mappings. It is followed by an array of ResTable_map structures.
@@ -1504,18 +1518,91 @@ struct ResTable_entry
// resources of the same name/type. This is only useful during
// linking with other resource tables.
FLAG_WEAK = 0x0004,
+ // If set, this is a compact entry with data type and value directly
+ // encoded in the this entry, see ResTable_entry::compact
+ FLAG_COMPACT = 0x0008,
};
- uint16_t flags;
-
- // Reference into ResTable_package::keyStrings identifying this entry.
- struct ResStringPool_ref key;
+
+ struct Full {
+ // Number of bytes in this structure.
+ uint16_t size;
+
+ uint16_t flags;
+
+ // Reference into ResTable_package::keyStrings identifying this entry.
+ struct ResStringPool_ref key;
+ } full;
+
+ /* A compact entry is indicated by FLAG_COMPACT, with flags at the same
+ * offset as a normal entry. This is only for simple data values where
+ *
+ * - size for entry or value can be inferred (both being 8 bytes).
+ * - key index is encoded in 16-bit
+ * - dataType is encoded as the higher 8-bit of flags
+ * - data is encoded directly in this entry
+ */
+ struct Compact {
+ uint16_t key;
+ uint16_t flags;
+ uint32_t data;
+ } compact;
+
+ uint16_t flags() const { return dtohs(full.flags); };
+ bool is_compact() const { return flags() & FLAG_COMPACT; }
+ bool is_complex() const { return flags() & FLAG_COMPLEX; }
+
+ size_t size() const {
+ return is_compact() ? sizeof(ResTable_entry) : dtohs(this->full.size);
+ }
+
+ uint32_t key() const {
+ return is_compact() ? dtohs(this->compact.key) : dtohl(this->full.key.index);
+ }
+
+ /* Always verify the memory associated with this entry and its value
+ * before calling value() or map_entry()
+ */
+ Res_value value() const {
+ Res_value v;
+ if (is_compact()) {
+ v.size = sizeof(Res_value);
+ v.res0 = 0;
+ v.data = dtohl(this->compact.data);
+ v.dataType = dtohs(compact.flags) >> 8;
+ } else {
+ auto vaddr = reinterpret_cast<const uint8_t*>(this) + dtohs(this->full.size);
+ auto value = reinterpret_cast<const Res_value*>(vaddr);
+ v.size = dtohs(value->size);
+ v.res0 = value->res0;
+ v.data = dtohl(value->data);
+ v.dataType = value->dataType;
+ }
+ return v;
+ }
+
+ const ResTable_map_entry* map_entry() const {
+ return is_complex() && !is_compact() ?
+ reinterpret_cast<const ResTable_map_entry*>(this) : nullptr;
+ }
};
+/* Make sure size of ResTable_entry::Full and ResTable_entry::Compact
+ * be the same as ResTable_entry. This is to allow iteration of entries
+ * to work in either cases.
+ *
+ * The offset of flags must be at the same place for both structures,
+ * to ensure the correct reading to decide whether this is a full entry
+ * or a compact entry.
+ */
+static_assert(sizeof(ResTable_entry) == sizeof(ResTable_entry::Full));
+static_assert(sizeof(ResTable_entry) == sizeof(ResTable_entry::Compact));
+static_assert(offsetof(ResTable_entry, full.flags) == offsetof(ResTable_entry, compact.flags));
+
/**
* Extended form of a ResTable_entry for map entries, defining a parent map
* resource from which to inherit values.
*/
-struct ResTable_map_entry : public ResTable_entry
+struct ResTable_map_entry : public ResTable_entry::Full
{
// Resource identifier of the parent mapping, or 0 if there is none.
// This is always treated as a TYPE_DYNAMIC_REFERENCE.
@@ -1746,6 +1833,28 @@ inline ResTable_overlayable_policy_header::PolicyFlags& operator |=(
return first;
}
+using ResourceId = uint32_t; // 0xpptteeee
+
+using DataType = uint8_t; // Res_value::dataType
+using DataValue = uint32_t; // Res_value::data
+
+struct OverlayManifestInfo {
+ std::string package_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+struct FabricatedOverlayEntryParameters {
+ std::string resource_name;
+ DataType data_type;
+ DataValue data_value;
+ std::string data_string_value;
+ std::optional<android::base::borrowed_fd> data_binary_value;
+ std::string configuration;
+};
+
class AssetManager2;
/**
@@ -1776,7 +1885,10 @@ public:
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- void addAlias(uint32_t stagedId, uint32_t finalizedId);
+ using AliasMap = std::vector<std::pair<uint32_t, uint32_t>>;
+ void setAliases(AliasMap aliases) {
+ mAliasId = std::move(aliases);
+ }
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
@@ -1790,12 +1902,12 @@ public:
return mEntries;
}
-private:
- uint8_t mAssignedPackageId;
- uint8_t mLookupTable[256];
- KeyedVector<String16, uint8_t> mEntries;
- bool mAppAsLib;
- std::map<uint32_t, uint32_t> mAliasId;
+ private:
+ uint8_t mLookupTable[256];
+ uint8_t mAssignedPackageId;
+ bool mAppAsLib;
+ KeyedVector<String16, uint8_t> mEntries;
+ AliasMap mAliasId;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index bd1c44033b88..2d90a526dfbe 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -25,14 +25,14 @@ namespace android {
// Extracts the package, type, and name from a string of the format: [[package:]type/]name
// Validation must be performed on each extracted piece.
// Returns false if there was a syntax error.
-bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
+bool ExtractResourceName(StringPiece str, StringPiece* out_package, StringPiece* out_type,
StringPiece* out_entry);
// Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName.
// Useful for getting resource name without re-running AssetManager2::FindEntry searches.
base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
- const StringPiece& package_name);
+ StringPiece package_name);
// Formats a ResourceName to "package:type/entry_name".
std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name);
diff --git a/libs/androidfw/include/androidfw/Source.h b/libs/androidfw/include/androidfw/Source.h
new file mode 100644
index 000000000000..ddc9ba421101
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Source.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SOURCE_H
+#define _ANDROID_SOURCE_H
+
+#include <optional>
+#include <ostream>
+#include <string>
+
+#include "android-base/stringprintf.h"
+#include "androidfw/StringPiece.h"
+
+namespace android {
+
+// Represents a file on disk. Used for logging and showing errors.
+struct Source {
+ std::string path;
+ std::optional<size_t> line;
+ std::optional<std::string> archive;
+
+ Source() = default;
+
+ inline Source(android::StringPiece path) : path(path) { // NOLINT(implicit)
+ }
+
+ inline Source(android::StringPiece path, android::StringPiece archive)
+ : path(path), archive(archive) {
+ }
+
+ inline Source(android::StringPiece path, size_t line) : path(path), line(line) {
+ }
+
+ inline Source WithLine(size_t line) const {
+ return Source(path, line);
+ }
+
+ std::string to_string() const {
+ std::string s = path;
+ if (archive) {
+ s = ::android::base::StringPrintf("%s@%s", archive.value().c_str(), s.c_str());
+ }
+ if (line) {
+ s = ::android::base::StringPrintf("%s:%zd", s.c_str(), line.value());
+ }
+ return s;
+ }
+};
+
+//
+// Implementations
+//
+
+inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
+ return out << source.to_string();
+}
+
+inline bool operator==(const Source& lhs, const Source& rhs) {
+ return lhs.path == rhs.path && lhs.line == rhs.line;
+}
+
+inline bool operator<(const Source& lhs, const Source& rhs) {
+ int cmp = lhs.path.compare(rhs.path);
+ if (cmp < 0) return true;
+ if (cmp > 0) return false;
+ if (lhs.line) {
+ if (rhs.line) {
+ return lhs.line.value() < rhs.line.value();
+ }
+ return false;
+ }
+ return bool(rhs.line);
+}
+
+} // namespace android
+
+#endif // _ANDROID_SOURCE_H
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 921877dc4982..f6cc64edfb5a 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -19,307 +19,37 @@
#include <ostream>
#include <string>
+#include <string_view>
-#include "utils/JenkinsHash.h"
#include "utils/Unicode.h"
namespace android {
-// Read only wrapper around basic C strings. Prevents excessive copying.
-// StringPiece does not own the data it is wrapping. The lifetime of the underlying
-// data must outlive this StringPiece.
-//
-// WARNING: When creating from std::basic_string<>, moving the original
-// std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
-// BasicStringPiece<> should only be used transitively.
-//
-// NOTE: When creating an std::pair<StringPiece, T> using std::make_pair(),
-// passing an std::string will first copy the string, then create a StringPiece
-// on the copy, which is then immediately destroyed.
-// Instead, create a StringPiece explicitly:
-//
-// std::string my_string = "foo";
-// std::make_pair<StringPiece, T>(StringPiece(my_string), ...);
-template <typename TChar>
-class BasicStringPiece {
- public:
- using const_iterator = const TChar*;
- using difference_type = size_t;
- using size_type = size_t;
-
- // End of string marker.
- constexpr static const size_t npos = static_cast<size_t>(-1);
-
- BasicStringPiece();
- BasicStringPiece(const BasicStringPiece<TChar>& str);
- BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(google-explicit-constructor)
- BasicStringPiece(const TChar* str); // NOLINT(google-explicit-constructor)
- BasicStringPiece(const TChar* str, size_t len);
-
- BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
- BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
-
- BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
- BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const;
-
- const TChar* data() const;
- size_t length() const;
- size_t size() const;
- bool empty() const;
- std::basic_string<TChar> to_string() const;
-
- bool contains(const BasicStringPiece<TChar>& rhs) const;
- int compare(const BasicStringPiece<TChar>& rhs) const;
- bool operator<(const BasicStringPiece<TChar>& rhs) const;
- bool operator>(const BasicStringPiece<TChar>& rhs) const;
- bool operator==(const BasicStringPiece<TChar>& rhs) const;
- bool operator!=(const BasicStringPiece<TChar>& rhs) const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
- private:
- const TChar* data_;
- size_t length_;
-};
+template <class T>
+using BasicStringPiece = std::basic_string_view<T>;
using StringPiece = BasicStringPiece<char>;
using StringPiece16 = BasicStringPiece<char16_t>;
-//
-// BasicStringPiece implementation.
-//
-
-template <typename TChar>
-constexpr const size_t BasicStringPiece<TChar>::npos;
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : data_(nullptr), length_(0) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str)
- : data_(str.data_), length_(str.length_) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str)
- : data_(str.data()), length_(str.length()) {}
-
-template <>
-inline BasicStringPiece<char>::BasicStringPiece(const char* str)
- : data_(str), length_(str != nullptr ? strlen(str) : 0) {}
-
-template <>
-inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str)
- : data_(str), length_(str != nullptr ? strlen16(str) : 0) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len)
- : data_(str), length_(len) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
- const BasicStringPiece<TChar>& rhs) {
- data_ = rhs.data_;
- length_ = rhs.length_;
- return *this;
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
- data_ = str;
- length_ = len;
- return *this;
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (len == npos) {
- len = length_ - start;
- }
-
- if (start > length_ || start + len > length_) {
- return BasicStringPiece<TChar>();
- }
- return BasicStringPiece<TChar>(data_ + start, len);
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
- BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const {
- return BasicStringPiece<TChar>(begin, end - begin);
-}
-
-template <typename TChar>
-inline const TChar* BasicStringPiece<TChar>::data() const {
- return data_;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::length() const {
- return length_;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::size() const {
- return length_;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::empty() const {
- return length_ == 0;
-}
-
-template <typename TChar>
-inline std::basic_string<TChar> BasicStringPiece<TChar>::to_string() const {
- return std::basic_string<TChar>(data_, length_);
-}
-
-template <>
-inline bool BasicStringPiece<char>::contains(const BasicStringPiece<char>& rhs) const {
- if (!data_ || !rhs.data_) {
- return false;
- }
- if (rhs.length_ > length_) {
- return false;
- }
- return strstr(data_, rhs.data_) != nullptr;
-}
-
-template <>
-inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
- const char nullStr = '\0';
- const char* b1 = data_ != nullptr ? data_ : &nullStr;
- const char* e1 = b1 + length_;
- const char* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
- const char* e2 = b2 + rhs.length_;
-
- while (b1 < e1 && b2 < e2) {
- const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
- if (d) {
- return d;
- }
- }
- return static_cast<int>(length_ - rhs.length_);
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
- const ssize_t result_len = utf16_to_utf8_length(str.data(), str.size());
- if (result_len < 0) {
- // Empty string.
- return out;
- }
-
- std::string result;
- result.resize(static_cast<size_t>(result_len));
- utf16_to_utf8(str.data(), str.length(), &*result.begin(), static_cast<size_t>(result_len) + 1);
- return out << result;
-}
-
-template <>
-inline bool BasicStringPiece<char16_t>::contains(const BasicStringPiece<char16_t>& rhs) const {
- if (!data_ || !rhs.data_) {
- return false;
- }
- if (rhs.length_ > length_) {
- return false;
- }
- return strstr16(data_, rhs.data_) != nullptr;
-}
-
-template <>
-inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
- const char16_t nullStr = u'\0';
- const char16_t* b1 = data_ != nullptr ? data_ : &nullStr;
- const char16_t* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
- return strzcmp16(b1, length_, b2, rhs.length_);
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) < 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) > 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) == 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) != 0;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
- return data_;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
- return data_ + length_;
-}
-
-template <typename TChar>
-inline bool operator==(const TChar* lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) == rhs;
-}
-
-template <typename TChar>
-inline bool operator!=(const TChar* lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) != rhs;
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
- return out.write(str.data(), str.size());
-}
-
-template <typename TChar>
-inline ::std::basic_string<TChar>& operator+=(::std::basic_string<TChar>& lhs,
- const BasicStringPiece<TChar>& rhs) {
- return lhs.append(rhs.data(), rhs.size());
-}
-
-template <typename TChar>
-inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return rhs == lhs;
-}
-
-template <typename TChar>
-inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return rhs != lhs;
-}
-
} // namespace android
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+namespace std {
+
+inline ::std::ostream& operator<<(::std::ostream& out, ::std::u16string_view str) {
ssize_t utf8_len = utf16_to_utf8_length(str.data(), str.size());
if (utf8_len < 0) {
- return out << "???";
+ return out; // empty
}
std::string utf8;
utf8.resize(static_cast<size_t>(utf8_len));
- utf16_to_utf8(str.data(), str.size(), &*utf8.begin(), utf8_len + 1);
+ utf16_to_utf8(str.data(), str.size(), utf8.data(), utf8_len + 1);
return out << utf8;
}
-namespace std {
-
-template <typename TChar>
-struct hash<android::BasicStringPiece<TChar>> {
- size_t operator()(const android::BasicStringPiece<TChar>& str) const {
- uint32_t hashCode = android::JenkinsHashMixBytes(
- 0, reinterpret_cast<const uint8_t*>(str.data()), sizeof(TChar) * str.size());
- return static_cast<size_t>(hashCode);
- }
-};
+inline ::std::ostream& operator<<(::std::ostream& out, const ::std::u16string& str) {
+ return out << std::u16string_view(str);
+}
} // namespace std
diff --git a/libs/androidfw/include/androidfw/StringPool.h b/libs/androidfw/include/androidfw/StringPool.h
new file mode 100644
index 000000000000..0190ab57bf23
--- /dev/null
+++ b/libs/androidfw/include/androidfw/StringPool.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_STRING_POOL_H
+#define _ANDROID_STRING_POOL_H
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "BigBuffer.h"
+#include "IDiagnostics.h"
+#include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/StringPiece.h"
+
+namespace android {
+
+struct Span {
+ std::string name;
+ uint32_t first_char;
+ uint32_t last_char;
+
+ bool operator==(const Span& right) const {
+ return name == right.name && first_char == right.first_char && last_char == right.last_char;
+ }
+};
+
+struct StyleString {
+ std::string str;
+ std::vector<Span> spans;
+};
+
+// A StringPool for storing the value of String and StyledString resources.
+// Styles and Strings are stored separately, since the runtime variant of this
+// class -- ResStringPool -- requires that styled strings *always* appear first, since their
+// style data is stored as an array indexed by the same indices as the main string pool array.
+// Otherwise, the style data array would have to be sparse and take up more space.
+class StringPool {
+ public:
+ using size_type = size_t;
+
+ class Context {
+ public:
+ enum : uint32_t {
+ kHighPriority = 1u,
+ kNormalPriority = 0x7fffffffu,
+ kLowPriority = 0xffffffffu,
+ };
+ uint32_t priority = kNormalPriority;
+ android::ConfigDescription config;
+
+ Context() = default;
+ Context(uint32_t p, const android::ConfigDescription& c) : priority(p), config(c) {
+ }
+ explicit Context(uint32_t p) : priority(p) {
+ }
+ explicit Context(const android::ConfigDescription& c) : priority(kNormalPriority), config(c) {
+ }
+ };
+
+ class Entry;
+
+ class Ref {
+ public:
+ Ref();
+ Ref(const Ref&);
+ ~Ref();
+
+ Ref& operator=(const Ref& rhs);
+ bool operator==(const Ref& rhs) const;
+ bool operator!=(const Ref& rhs) const;
+ const std::string* operator->() const;
+ const std::string& operator*() const;
+
+ size_t index() const;
+ const Context& GetContext() const;
+
+ private:
+ friend class StringPool;
+
+ explicit Ref(Entry* entry);
+
+ Entry* entry_;
+ };
+
+ class StyleEntry;
+
+ class StyleRef {
+ public:
+ StyleRef();
+ StyleRef(const StyleRef&);
+ ~StyleRef();
+
+ StyleRef& operator=(const StyleRef& rhs);
+ bool operator==(const StyleRef& rhs) const;
+ bool operator!=(const StyleRef& rhs) const;
+ const StyleEntry* operator->() const;
+ const StyleEntry& operator*() const;
+
+ size_t index() const;
+ const Context& GetContext() const;
+
+ private:
+ friend class StringPool;
+
+ explicit StyleRef(StyleEntry* entry);
+
+ StyleEntry* entry_;
+ };
+
+ class Entry {
+ public:
+ std::string value;
+ Context context;
+
+ private:
+ friend class StringPool;
+ friend class Ref;
+
+ size_t index_;
+ int ref_;
+ const StringPool* pool_;
+ };
+
+ struct Span {
+ Ref name;
+ uint32_t first_char;
+ uint32_t last_char;
+ };
+
+ class StyleEntry {
+ public:
+ std::string value;
+ Context context;
+ std::vector<Span> spans;
+
+ private:
+ friend class StringPool;
+ friend class StyleRef;
+
+ size_t index_;
+ int ref_;
+ };
+
+ static bool FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag);
+ static bool FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag);
+
+ StringPool() = default;
+ StringPool(StringPool&&) = default;
+ StringPool& operator=(StringPool&&) = default;
+
+ // Adds a string to the pool, unless it already exists. Returns a reference to the string in the
+ // pool.
+ Ref MakeRef(android::StringPiece str);
+
+ // Adds a string to the pool, unless it already exists, with a context object that can be used
+ // when sorting the string pool. Returns a reference to the string in the pool.
+ Ref MakeRef(android::StringPiece str, const Context& context);
+
+ // Adds a string from another string pool. Returns a reference to the string in the string pool.
+ Ref MakeRef(const Ref& ref);
+
+ // Adds a style to the string pool and returns a reference to it.
+ StyleRef MakeRef(const StyleString& str);
+
+ // Adds a style to the string pool with a context object that can be used when sorting the string
+ // pool. Returns a reference to the style in the string pool.
+ StyleRef MakeRef(const StyleString& str, const Context& context);
+
+ // Adds a style from another string pool. Returns a reference to the style in the string pool.
+ StyleRef MakeRef(const StyleRef& ref);
+
+ // Moves pool into this one without coalescing strings. When this function returns, pool will be
+ // empty.
+ void Merge(StringPool&& pool);
+
+ inline const std::vector<std::unique_ptr<Entry>>& strings() const {
+ return strings_;
+ }
+
+ // Returns the number of strings in the table.
+ inline size_t size() const {
+ return styles_.size() + strings_.size();
+ }
+
+ // Reserves space for strings and styles as an optimization.
+ void HintWillAdd(size_t string_count, size_t style_count);
+
+ // Sorts the strings according to their Context using some comparison function.
+ // Equal Contexts are further sorted by string value, lexicographically.
+ // If no comparison function is provided, values are only sorted lexicographically.
+ void Sort(const std::function<int(const Context&, const Context&)>& cmp = nullptr);
+
+ // Removes any strings that have no references.
+ void Prune();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringPool);
+
+ static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
+
+ Ref MakeRefImpl(android::StringPiece str, const Context& context, bool unique);
+ void ReAssignIndices();
+
+ std::vector<std::unique_ptr<Entry>> strings_;
+ std::vector<std::unique_ptr<StyleEntry>> styles_;
+ std::unordered_multimap<android::StringPiece, Entry*> indexed_strings_;
+};
+
+} // namespace android
+
+#endif // _ANDROID_STRING_POOL_H
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index c59b5b6c51a2..a188abb7ecb5 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -17,20 +17,29 @@
#ifndef UTIL_H_
#define UTIL_H_
+#include <android-base/macros.h>
+#include <util/map_ptr.h>
+
#include <cstdlib>
#include <memory>
-#include <sstream>
#include <vector>
-#include <android-base/macros.h>
-#include <util/map_ptr.h>
-
+#include "androidfw/BigBuffer.h"
+#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
+#include "utils/ByteOrder.h"
#ifdef __ANDROID__
#define ANDROID_LOG(x) LOG(x)
#else
-#define ANDROID_LOG(x) std::stringstream()
+namespace android {
+// No default logging for aapt2, as it's too noisy for a command line dev tool.
+struct NullLogger {
+ template <class T>
+ friend const NullLogger& operator<<(const NullLogger& l, const T&) { return l; }
+};
+}
+#define ANDROID_LOG(x) (android::NullLogger{})
#endif
namespace android {
@@ -46,95 +55,67 @@ std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
-// Based on std::unique_ptr, but uses free() to release malloc'ed memory
-// without incurring the size increase of holding on to a custom deleter.
-template <typename T>
-class unique_cptr {
- public:
- using pointer = typename std::add_pointer<T>::type;
-
- constexpr unique_cptr() : ptr_(nullptr) {}
- constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
- explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
- unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
-
- ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
-
- inline unique_cptr& operator=(unique_cptr&& o) noexcept {
- if (&o == this) {
- return *this;
- }
-
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = o.ptr_;
- o.ptr_ = nullptr;
- return *this;
- }
-
- inline unique_cptr& operator=(std::nullptr_t) {
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = nullptr;
- return *this;
- }
-
- pointer release() {
- pointer result = ptr_;
- ptr_ = nullptr;
- return result;
- }
-
- inline pointer get() const { return ptr_; }
-
- void reset(pointer ptr = pointer()) {
- if (ptr == ptr_) {
- return;
- }
-
- pointer old_ptr = ptr_;
- ptr_ = ptr;
- std::free(reinterpret_cast<void*>(old_ptr));
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory.
+struct FreeDeleter {
+ void operator()(void* ptr) const {
+ ::free(ptr);
}
+};
+template <typename T>
+using unique_cptr = std::unique_ptr<T, FreeDeleter>;
- inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
-
- inline explicit operator bool() const { return ptr_ != nullptr; }
-
- inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
-
- inline pointer operator->() const { return ptr_; }
+void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
- inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
+// Converts a UTF-8 string to a UTF-16 string.
+std::u16string Utf8ToUtf16(StringPiece utf8);
- inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
+// Converts a UTF-16 string to a UTF-8 string.
+std::string Utf16ToUtf8(StringPiece16 utf16);
- inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
+// Converts a UTF8 string into Modified UTF8
+std::string Utf8ToModifiedUtf8(std::string_view utf8);
- inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
+// Converts a Modified UTF8 string into a UTF8 string
+std::string ModifiedUtf8ToUtf8(std::string_view modified_utf8);
- private:
- DISALLOW_COPY_AND_ASSIGN(unique_cptr);
+inline uint16_t HostToDevice16(uint16_t value) {
+ return htods(value);
+}
- pointer ptr_;
-};
+inline uint32_t HostToDevice32(uint32_t value) {
+ return htodl(value);
+}
-void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
+inline uint16_t DeviceToHost16(uint16_t value) {
+ return dtohs(value);
+}
-// Converts a UTF-8 string to a UTF-16 string.
-std::u16string Utf8ToUtf16(const StringPiece& utf8);
+inline uint32_t DeviceToHost32(uint32_t value) {
+ return dtohl(value);
+}
-// Converts a UTF-16 string to a UTF-8 string.
-std::string Utf16ToUtf8(const StringPiece16& utf16);
+std::vector<std::string> SplitAndLowercase(android::StringPiece str, char sep);
-std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
+inline bool IsFourByteAligned(const void* data) {
+ return ((uintptr_t)data & 0x3U) == 0;
+}
template <typename T>
inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
- return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+ return IsFourByteAligned(data.unsafe_ptr());
}
-inline bool IsFourByteAligned(const void* data) {
- return ((size_t)data & 0x3U) == 0;
-}
+// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+// the conversion to UTF-16 happens within ResStringPool.
+android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
+
+// Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16,
+// the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method,
+// which maintains no state or cache. This means we must return an std::string copy.
+std::string GetString(const android::ResStringPool& pool, size_t idx);
+
+// Copies the entire BigBuffer into a single buffer.
+std::unique_ptr<uint8_t[]> Copy(const android::BigBuffer& buffer);
} // namespace util
} // namespace android
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 5a5a0e29125d..d40d24ede769 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -44,6 +44,10 @@ FileType getFileType(const char* fileName);
/* get the file's modification date; returns -1 w/errno set on failure */
time_t getFileModDate(const char* fileName);
+// Check if |path| or |fd| resides on a readonly filesystem.
+bool isReadonlyFilesystem(const char* path);
+bool isReadonlyFilesystem(int fd);
+
}; // namespace android
#endif // _LIBS_ANDROID_FW_MISC_H
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 52854205207c..d3949e9cf69f 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -21,12 +21,17 @@
//
#include <androidfw/misc.h>
-#include <sys/stat.h>
+#include "android-base/logging.h"
+
+#ifdef __linux__
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#endif // __linux__
+
#include <cstring>
-#include <errno.h>
#include <cstdio>
-
-using namespace android;
+#include <errno.h>
+#include <sys/stat.h>
namespace android {
@@ -41,8 +46,7 @@ FileType getFileType(const char* fileName)
if (errno == ENOENT || errno == ENOTDIR)
return kFileTypeNonexistent;
else {
- fprintf(stderr, "getFileType got errno=%d on '%s'\n",
- errno, fileName);
+ PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
return kFileTypeUnknown;
}
} else {
@@ -82,4 +86,32 @@ time_t getFileModDate(const char* fileName)
return sb.st_mtime;
}
+#ifndef __linux__
+// No need to implement these on the host, the functions only matter on a device.
+bool isReadonlyFilesystem(const char*) {
+ return false;
+}
+bool isReadonlyFilesystem(int) {
+ return false;
+}
+#else // __linux__
+bool isReadonlyFilesystem(const char* path) {
+ struct statfs sfs;
+ if (::statfs(path, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+
+bool isReadonlyFilesystem(int fd) {
+ struct statfs sfs;
+ if (::fstatfs(fd, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): fstatfs(" << fd << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+#endif // __linux__
+
}; // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
index ddd8ab820cb1..1c89c61c8f78 100644
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -120,8 +120,8 @@ static void BM_ApplyStyleFramework(benchmark::State& state) {
return;
}
- std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie,
- Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset =
+ assetmanager.OpenNonAsset(std::string(*layout_path), value->cookie, Asset::ACCESS_BUFFER);
if (asset == nullptr) {
state.SkipWithError("failed to load layout");
return;
diff --git a/libs/androidfw/tests/BigBuffer_test.cpp b/libs/androidfw/tests/BigBuffer_test.cpp
new file mode 100644
index 000000000000..382d21e20846
--- /dev/null
+++ b/libs/androidfw/tests/BigBuffer_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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 "androidfw/BigBuffer.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using ::testing::NotNull;
+
+namespace android {
+
+TEST(BigBufferTest, AllocateSingleBlock) {
+ BigBuffer buffer(4);
+
+ EXPECT_THAT(buffer.NextBlock<char>(2), NotNull());
+ EXPECT_EQ(2u, buffer.size());
+}
+
+TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
+ BigBuffer buffer(16);
+
+ char* b1 = buffer.NextBlock<char>(8);
+ EXPECT_THAT(b1, NotNull());
+
+ char* b2 = buffer.NextBlock<char>(4);
+ EXPECT_THAT(b2, NotNull());
+
+ EXPECT_EQ(b1 + 8, b2);
+}
+
+TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
+ BigBuffer buffer(16);
+
+ EXPECT_THAT(buffer.NextBlock<char>(32), NotNull());
+ EXPECT_EQ(32u, buffer.size());
+}
+
+TEST(BigBufferTest, AppendAndMoveBlock) {
+ BigBuffer buffer(16);
+
+ uint32_t* b1 = buffer.NextBlock<uint32_t>();
+ ASSERT_THAT(b1, NotNull());
+ *b1 = 33;
+
+ {
+ BigBuffer buffer2(16);
+ b1 = buffer2.NextBlock<uint32_t>();
+ ASSERT_THAT(b1, NotNull());
+ *b1 = 44;
+
+ buffer.AppendBuffer(std::move(buffer2));
+ EXPECT_EQ(0u, buffer2.size()); // NOLINT
+ EXPECT_EQ(buffer2.begin(), buffer2.end());
+ }
+
+ EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
+
+ auto b = buffer.begin();
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
+
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
+
+ ASSERT_EQ(b, buffer.end());
+}
+
+TEST(BigBufferTest, PadAndAlignProperly) {
+ BigBuffer buffer(16);
+
+ ASSERT_THAT(buffer.NextBlock<char>(2), NotNull());
+ ASSERT_EQ(2u, buffer.size());
+ buffer.Pad(2);
+ ASSERT_EQ(4u, buffer.size());
+ buffer.Align4();
+ ASSERT_EQ(4u, buffer.size());
+ buffer.Pad(2);
+ ASSERT_EQ(6u, buffer.size());
+ buffer.Align4();
+ ASSERT_EQ(8u, buffer.size());
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
index 5d464c7dc0f7..9c36cfb212c5 100644
--- a/libs/androidfw/tests/ByteBucketArray_test.cpp
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -52,4 +52,57 @@ TEST(ByteBucketArrayTest, TestSparseInsertion) {
}
}
+TEST(ByteBucketArrayTest, TestForEach) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ int count = 0;
+ bba.forEachItem([&count](auto i, auto val) {
+ ++count;
+ switch (i) {
+ case 0:
+ EXPECT_EQ(1, val);
+ break;
+ case 10:
+ EXPECT_EQ(2, val);
+ break;
+ case 26:
+ EXPECT_EQ(3, val);
+ break;
+ case 129:
+ EXPECT_EQ(4, val);
+ break;
+ case 234:
+ EXPECT_EQ(5, val);
+ break;
+ default:
+ EXPECT_EQ(0, val);
+ break;
+ }
+ });
+ ASSERT_EQ(4 * 16, count);
+}
+
+TEST(ByteBucketArrayTest, TestTrimBuckets) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(255, 2));
+ {
+ bba.trimBuckets([](auto val) { return val < 2; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(1 * 16, count);
+ }
+ {
+ bba.trimBuckets([](auto val) { return val < 3; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(0, count);
+ }
+}
+
} // namespace android
diff --git a/libs/androidfw/tests/ConfigDescription_test.cpp b/libs/androidfw/tests/ConfigDescription_test.cpp
index ce7f8054e2ca..8fed0a4d22fc 100644
--- a/libs/androidfw/tests/ConfigDescription_test.cpp
+++ b/libs/androidfw/tests/ConfigDescription_test.cpp
@@ -25,8 +25,8 @@
namespace android {
-static ::testing::AssertionResult TestParse(
- const StringPiece& input, ConfigDescription* config = nullptr) {
+static ::testing::AssertionResult TestParse(StringPiece input,
+ ConfigDescription* config = nullptr) {
if (ConfigDescription::Parse(input, config)) {
return ::testing::AssertionSuccess() << input << " was successfully parsed";
}
@@ -138,7 +138,7 @@ TEST(ConfigDescriptionTest, ParseVrAttribute) {
EXPECT_EQ(std::string("vrheadset-v26"), config.toString().string());
}
-static inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) {
+static inline ConfigDescription ParseConfigOrDie(android::StringPiece str) {
ConfigDescription config;
CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration: " << str;
return config;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index d214e2dfef7b..c90ec197b5ef 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -71,62 +71,6 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) {
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
}
-TEST(LoadedArscTest, LoadSparseEntryApp) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
- &contents));
-
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
- contents.length());
- ASSERT_THAT(loaded_arsc, NotNull());
-
- const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
- ASSERT_THAT(package, NotNull());
-
- const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1;
- const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9);
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
-
- auto type = type_spec->type_entries[0];
- ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
-}
-
-TEST(LoadedArscTest, FindSparseEntryApp) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
- &contents));
-
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
- contents.length());
- ASSERT_THAT(loaded_arsc, NotNull());
-
- const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26));
- ASSERT_THAT(package, NotNull());
-
- const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1;
- const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26);
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
-
- // Ensure that AAPT2 sparsely encoded the v26 config as expected.
- auto type_entry = std::find_if(
- type_spec->type_entries.begin(), type_spec->type_entries.end(),
- [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; });
- ASSERT_NE(type_entry, type_spec->type_entries.end());
- ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0);
-
- // Test fetching a resource with only sparsely encoded configs by name.
- auto id = package->FindEntryByName(u"string", u"only_v26");
- ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0));
-}
-
TEST(LoadedArscTest, LoadSharedLibrary) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
@@ -404,4 +348,84 @@ TEST(LoadedArscTest, LoadCustomLoader) {
// sizeof(Res_value) might not be backwards compatible.
// TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+class LoadedArscParameterizedTest :
+ public testing::TestWithParam<std::string> {
+};
+
+TEST_P(LoadedArscParameterizedTest, LoadSparseEntryApp) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
+ ASSERT_THAT(package, NotNull());
+
+ const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1;
+ const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+ auto type = type_spec->type_entries[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
+}
+
+TEST_P(LoadedArscParameterizedTest, FindSparseEntryApp) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_land));
+ ASSERT_THAT(package, NotNull());
+
+ const uint8_t type_index = get_type_id(sparse::R::string::only_land) - 1;
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+ // Type Entry with default orientation is not sparse encoded because the ratio of
+ // populated entries to total entries is above threshold.
+ // Only find out default locale because Soong build system will introduce pseudo
+ // locales for the apk generated at runtime.
+ auto type_entry_default = std::find_if(
+ type_spec->type_entries.begin(), type_spec->type_entries.end(),
+ [] (const TypeSpec::TypeEntry& x) { return x.config.orientation == 0 &&
+ x.config.locale == 0; });
+ ASSERT_NE(type_entry_default, type_spec->type_entries.end());
+ ASSERT_EQ(type_entry_default->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+ // Type Entry with land orientation is sparse encoded as expected.
+ // Only find out default locale because Soong build system will introduce pseudo
+ // locales for the apk generated at runtime.
+ auto type_entry_land = std::find_if(
+ type_spec->type_entries.begin(), type_spec->type_entries.end(),
+ [](const TypeSpec::TypeEntry& x) { return x.config.orientation ==
+ ResTable_config::ORIENTATION_LAND &&
+ x.config.locale == 0; });
+ ASSERT_NE(type_entry_land, type_spec->type_entries.end());
+ ASSERT_NE(type_entry_land->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+ // Test fetching a resource with only sparsely encoded configs by name.
+ auto id = package->FindEntryByName(u"string", u"only_land");
+ ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_land, 0));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FrameWorkResourcesLoadedArscTests,
+ LoadedArscParameterizedTest,
+ ::testing::Values(
+ base::GetExecutableDirectory() + "/tests/data/sparse/sparse.apk",
+ base::GetExecutableDirectory() + "/FrameworkResourcesSparseTestApp.apk"
+ ));
+
} // namespace android
diff --git a/libs/androidfw/tests/PosixUtils_test.cpp b/libs/androidfw/tests/PosixUtils_test.cpp
index 8c49350796ec..097e6b0bba65 100644
--- a/libs/androidfw/tests/PosixUtils_test.cpp
+++ b/libs/androidfw/tests/PosixUtils_test.cpp
@@ -28,27 +28,27 @@ namespace util {
TEST(PosixUtilsTest, AbsolutePathToBinary) {
const auto result = ExecuteBinary({"/bin/date", "--help"});
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, 0);
- ASSERT_GE(result->stdout_str.find("usage: date "), 0);
+ ASSERT_TRUE((bool)result);
+ ASSERT_EQ(result.status, 0);
+ ASSERT_GE(result.stdout_str.find("usage: date "), 0);
}
TEST(PosixUtilsTest, RelativePathToBinary) {
const auto result = ExecuteBinary({"date", "--help"});
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, 0);
- ASSERT_GE(result->stdout_str.find("usage: date "), 0);
+ ASSERT_TRUE((bool)result);
+ ASSERT_EQ(result.status, 0);
+ ASSERT_GE(result.stdout_str.find("usage: date "), 0);
}
TEST(PosixUtilsTest, BadParameters) {
const auto result = ExecuteBinary({"/bin/date", "--this-parameter-is-not-supported"});
- ASSERT_THAT(result, NotNull());
- ASSERT_NE(result->status, 0);
+ ASSERT_TRUE((bool)result);
+ ASSERT_GT(result.status, 0);
}
TEST(PosixUtilsTest, NoSuchBinary) {
const auto result = ExecuteBinary({"/this/binary/does/not/exist"});
- ASSERT_THAT(result, IsNull());
+ ASSERT_FALSE((bool)result);
}
} // android
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 9aeb00c47e63..fbf70981f2de 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -15,6 +15,7 @@
*/
#include "androidfw/ResourceTypes.h"
+#include "android-base/file.h"
#include <codecvt>
#include <locale>
@@ -41,34 +42,6 @@ TEST(ResTableTest, ShouldLoadSuccessfully) {
ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
}
-TEST(ResTableTest, ShouldLoadSparseEntriesSuccessfully) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
- &contents));
-
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
-
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
- table.setParameters(&config);
-
- String16 name(u"com.android.sparse:integer/foo_9");
- uint32_t flags;
- uint32_t resid =
- table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags);
- ASSERT_NE(0u, resid);
-
- Res_value val;
- ResTable_config selected_config;
- ASSERT_GE(
- table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config),
- 0);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- EXPECT_EQ(900u, val.data);
-}
-
TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
@@ -476,4 +449,43 @@ TEST(ResTableTest, TruncatedEncodeLength) {
ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value());
}
+class ResTableParameterizedTest :
+ public testing::TestWithParam<std::string> {
+};
+
+TEST_P(ResTableParameterizedTest, ShouldLoadSparseEntriesSuccessfully) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.orientation = ResTable_config::ORIENTATION_LAND;
+ table.setParameters(&config);
+
+ String16 name(u"com.android.sparse:integer/foo_9");
+ uint32_t flags;
+ uint32_t resid =
+ table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags);
+ ASSERT_NE(0u, resid);
+
+ Res_value val;
+ ResTable_config selected_config;
+ ASSERT_GE(
+ table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config),
+ 0);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ EXPECT_EQ(900u, val.data);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FrameWorkResourcesResTableTests,
+ ResTableParameterizedTest,
+ ::testing::Values(
+ base::GetExecutableDirectory() + "/tests/data/sparse/sparse.apk",
+ base::GetExecutableDirectory() + "/FrameworkResourcesSparseTestApp.apk"
+ ));
+
} // namespace android
diff --git a/libs/androidfw/tests/ResourceTimer_test.cpp b/libs/androidfw/tests/ResourceTimer_test.cpp
new file mode 100644
index 000000000000..4a1e9735de7a
--- /dev/null
+++ b/libs/androidfw/tests/ResourceTimer_test.cpp
@@ -0,0 +1,245 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <androidfw/Util.h>
+
+#include "TestHelpers.h"
+
+#include <androidfw/ResourceTimer.h>
+
+namespace android {
+
+namespace {
+
+// Create a reading in us. This is a convenience function to avoid multiplying by 1000
+// everywhere.
+unsigned int US(int us) {
+ return us * 1000;
+}
+
+}
+
+TEST(ResourceTimerTest, TimerBasic) {
+ ResourceTimer::Timer timer;
+ ASSERT_THAT(timer.count, 0);
+ ASSERT_THAT(timer.total, 0);
+
+ for (int i = 1; i <= 100; i++) {
+ timer.record(US(i));
+ }
+ ASSERT_THAT(timer.count, 100);
+ ASSERT_THAT(timer.total, US((101 * 100)/2));
+ ASSERT_THAT(timer.mintime, US(1));
+ ASSERT_THAT(timer.maxtime, US(100));
+ ASSERT_THAT(timer.pvalues.p50.floor, 0);
+ ASSERT_THAT(timer.pvalues.p50.nominal, 0);
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+
+ // Test reset functionality. All values should be zero after the reset. Computing pvalues
+ // after the result should also yield zeros.
+ timer.reset();
+ ASSERT_THAT(timer.count, 0);
+ ASSERT_THAT(timer.total, 0);
+ ASSERT_THAT(timer.mintime, US(0));
+ ASSERT_THAT(timer.maxtime, US(0));
+ ASSERT_THAT(timer.pvalues.p50.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(timer.largest[0], US(0));
+ ASSERT_THAT(timer.largest[1], US(0));
+ ASSERT_THAT(timer.largest[2], US(0));
+ ASSERT_THAT(timer.largest[3], US(0));
+ ASSERT_THAT(timer.largest[4], US(0));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(0));
+
+ // Test again, adding elements in reverse.
+ for (int i = 100; i >= 1; i--) {
+ timer.record(US(i));
+ }
+ ASSERT_THAT(timer.count, 100);
+ ASSERT_THAT(timer.total, US((101 * 100)/2));
+ ASSERT_THAT(timer.mintime, US(1));
+ ASSERT_THAT(timer.maxtime, US(100));
+ ASSERT_THAT(timer.pvalues.p50.floor, 0);
+ ASSERT_THAT(timer.pvalues.p50.nominal, 0);
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+}
+
+TEST(ResourceTimerTest, TimerLimit) {
+ ResourceTimer::Timer timer;
+
+ // Event truncation means that a time of 1050us will be stored in the 1000us
+ // bucket. Since there is a single event, all p-values lie in the same range.
+ timer.record(US(1050));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(1000));
+}
+
+TEST(ResourceTimerTest, TimerCopy) {
+ ResourceTimer::Timer source;
+ for (int i = 1; i <= 100; i++) {
+ source.record(US(i));
+ }
+ ResourceTimer::Timer timer;
+ ResourceTimer::Timer::copy(timer, source, true);
+ ASSERT_THAT(source.count, 0);
+ ASSERT_THAT(source.total, 0);
+ // compute() is not normally be called on a reset timer, but it should work and it should return
+ // all zeros.
+ source.compute();
+ ASSERT_THAT(source.pvalues.p50.floor, US(0));
+ ASSERT_THAT(source.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p90.floor, US(0));
+ ASSERT_THAT(source.pvalues.p90.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p95.floor, US(0));
+ ASSERT_THAT(source.pvalues.p95.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p99.floor, US(0));
+ ASSERT_THAT(source.pvalues.p99.nominal, US(0));
+ ASSERT_THAT(source.largest[0], US(0));
+ ASSERT_THAT(source.largest[1], US(0));
+ ASSERT_THAT(source.largest[2], US(0));
+ ASSERT_THAT(source.largest[3], US(0));
+ ASSERT_THAT(source.largest[4], US(0));
+
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+
+ // Call compute a second time. The values must be the same.
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+
+ // Modify the source. If timer and source share histogram arrays, this will introduce an
+ // error.
+ for (int i = 1; i <= 100; i++) {
+ source.record(US(i));
+ }
+ // Call compute a third time. The values must be the same.
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+}
+
+// Verify that if too many oversize entries are reported, the percentile values cannot be computed
+// and are set to zero.
+TEST(ResourceTimerTest, TimerOversize) {
+ static const int oversize = US(2 * 1000 * 1000);
+
+ ResourceTimer::Timer timer;
+ for (int i = 1; i <= 100; i++) {
+ timer.record(US(i));
+ }
+
+ // Insert enough oversize values to invalidate the p90, p95, and p99 percentiles. The p50 is
+ // still computable.
+ for (int i = 1; i <= 50; i++) {
+ timer.record(oversize);
+ }
+ ASSERT_THAT(timer.largest[0], oversize);
+ ASSERT_THAT(timer.largest[1], oversize);
+ ASSERT_THAT(timer.largest[2], oversize);
+ ASSERT_THAT(timer.largest[3], oversize);
+ ASSERT_THAT(timer.largest[4], oversize);
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(74));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(75));
+ ASSERT_THAT(timer.pvalues.p90.floor, 0);
+ ASSERT_THAT(timer.pvalues.p90.nominal, 0);
+ ASSERT_THAT(timer.pvalues.p95.floor, 0);
+ ASSERT_THAT(timer.pvalues.p95.nominal, 0);
+ ASSERT_THAT(timer.pvalues.p99.floor, 0);
+ ASSERT_THAT(timer.pvalues.p99.nominal, 0);
+}
+
+
+} // namespace android
diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp
index c9b4ad8af278..fffeeb802873 100644
--- a/libs/androidfw/tests/SparseEntry_bench.cpp
+++ b/libs/androidfw/tests/SparseEntry_bench.cpp
@@ -16,6 +16,7 @@
#include "androidfw/AssetManager.h"
#include "androidfw/ResourceTypes.h"
+#include "android-base/file.h"
#include "BenchmarkHelpers.h"
#include "data/sparse/R.h"
@@ -24,40 +25,74 @@ namespace sparse = com::android::sparse;
namespace android {
+static void BM_SparseEntryGetResourceHelper(const std::vector<std::string>& paths,
+ uint32_t resid, benchmark::State& state, void (*GetResourceBenchmarkFunc)(
+ const std::vector<std::string>&, const ResTable_config*,
+ uint32_t, benchmark::State&)){
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.orientation = ResTable_config::ORIENTATION_LAND;
+ GetResourceBenchmarkFunc(paths, &config, resid, state);
+}
+
static void BM_SparseEntryGetResourceOldSparse(benchmark::State& state, uint32_t resid) {
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
- GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state);
+ BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/sparse.apk"}, resid,
+ state, &GetResourceBenchmarkOld);
}
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Small, sparse::R::integer::foo_9);
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Large, sparse::R::string::foo_999);
static void BM_SparseEntryGetResourceOldNotSparse(benchmark::State& state, uint32_t resid) {
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
- GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state);
+ BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/not_sparse.apk"}, resid,
+ state, &GetResourceBenchmarkOld);
}
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Small, sparse::R::integer::foo_9);
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Large, sparse::R::string::foo_999);
static void BM_SparseEntryGetResourceSparse(benchmark::State& state, uint32_t resid) {
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
- GetResourceBenchmark({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state);
+ BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/sparse.apk"}, resid,
+ state, &GetResourceBenchmark);
}
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Small, sparse::R::integer::foo_9);
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Large, sparse::R::string::foo_999);
static void BM_SparseEntryGetResourceNotSparse(benchmark::State& state, uint32_t resid) {
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
- GetResourceBenchmark({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state);
+ BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/not_sparse.apk"}, resid,
+ state, &GetResourceBenchmark);
}
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Small, sparse::R::integer::foo_9);
BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Large, sparse::R::string::foo_999);
+static void BM_SparseEntryGetResourceOldSparseRuntime(benchmark::State& state, uint32_t resid) {
+ BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() +
+ "/FrameworkResourcesSparseTestApp.apk"}, resid, state,
+ &GetResourceBenchmarkOld);
+}
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparseRuntime, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparseRuntime, Large, sparse::R::string::foo_999);
+
+static void BM_SparseEntryGetResourceOldNotSparseRuntime(benchmark::State& state, uint32_t resid) {
+ BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() +
+ "/FrameworkResourcesNotSparseTestApp.apk"}, resid, state,
+ &GetResourceBenchmarkOld);
+}
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparseRuntime, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparseRuntime, Large, sparse::R::string::foo_999);
+
+static void BM_SparseEntryGetResourceSparseRuntime(benchmark::State& state, uint32_t resid) {
+ BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() +
+ "/FrameworkResourcesSparseTestApp.apk"}, resid, state,
+ &GetResourceBenchmark);
+}
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparseRuntime, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparseRuntime, Large, sparse::R::string::foo_999);
+
+static void BM_SparseEntryGetResourceNotSparseRuntime(benchmark::State& state, uint32_t resid) {
+ BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() +
+ "/FrameworkResourcesNotSparseTestApp.apk"}, resid, state,
+ &GetResourceBenchmark);
+}
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparseRuntime, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparseRuntime, Large, sparse::R::string::foo_999);
+
} // namespace android
diff --git a/libs/androidfw/tests/StringPiece_test.cpp b/libs/androidfw/tests/StringPiece_test.cpp
index 316a5c1bf40e..822e527253df 100644
--- a/libs/androidfw/tests/StringPiece_test.cpp
+++ b/libs/androidfw/tests/StringPiece_test.cpp
@@ -60,36 +60,4 @@ TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
EXPECT_TRUE(StringPiece(car) > banana);
}
-TEST(StringPieceTest, ContainsOtherStringPiece) {
- StringPiece text("I am a leaf on the wind.");
- StringPiece start_needle("I am");
- StringPiece end_needle("wind.");
- StringPiece middle_needle("leaf");
- StringPiece empty_needle("");
- StringPiece missing_needle("soar");
- StringPiece long_needle("This string is longer than the text.");
-
- EXPECT_TRUE(text.contains(start_needle));
- EXPECT_TRUE(text.contains(end_needle));
- EXPECT_TRUE(text.contains(middle_needle));
- EXPECT_TRUE(text.contains(empty_needle));
- EXPECT_FALSE(text.contains(missing_needle));
- EXPECT_FALSE(text.contains(long_needle));
-
- StringPiece16 text16(u"I am a leaf on the wind.");
- StringPiece16 start_needle16(u"I am");
- StringPiece16 end_needle16(u"wind.");
- StringPiece16 middle_needle16(u"leaf");
- StringPiece16 empty_needle16(u"");
- StringPiece16 missing_needle16(u"soar");
- StringPiece16 long_needle16(u"This string is longer than the text.");
-
- EXPECT_TRUE(text16.contains(start_needle16));
- EXPECT_TRUE(text16.contains(end_needle16));
- EXPECT_TRUE(text16.contains(middle_needle16));
- EXPECT_TRUE(text16.contains(empty_needle16));
- EXPECT_FALSE(text16.contains(missing_needle16));
- EXPECT_FALSE(text16.contains(long_needle16));
-}
-
} // namespace android
diff --git a/libs/androidfw/tests/StringPool_test.cpp b/libs/androidfw/tests/StringPool_test.cpp
new file mode 100644
index 000000000000..0e0acae165d9
--- /dev/null
+++ b/libs/androidfw/tests/StringPool_test.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2015 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 "androidfw/StringPool.h"
+
+#include <string>
+
+#include "androidfw/IDiagnostics.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using ::android::StringPiece;
+using ::android::StringPiece16;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::NotNull;
+using ::testing::Pointee;
+
+namespace android {
+
+TEST(StringPoolTest, InsertOneString) {
+ StringPool pool;
+
+ StringPool::Ref ref = pool.MakeRef("wut");
+ EXPECT_THAT(*ref, Eq("wut"));
+}
+
+TEST(StringPoolTest, InsertTwoUniqueStrings) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("wut");
+ StringPool::Ref ref_b = pool.MakeRef("hey");
+
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("hey"));
+}
+
+TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("wut");
+ StringPool::Ref ref_b = pool.MakeRef("wut");
+
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(1u));
+}
+
+TEST(StringPoolTest, DoNotDedupeSameStringDifferentPriority) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("wut", StringPool::Context(0x81010001));
+ StringPool::Ref ref_b = pool.MakeRef("wut", StringPool::Context(0x81010002));
+
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(2u));
+}
+
+TEST(StringPoolTest, MaintainInsertionOrderIndex) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
+
+ EXPECT_THAT(ref_a.index(), Eq(0u));
+ EXPECT_THAT(ref_b.index(), Eq(1u));
+ EXPECT_THAT(ref_c.index(), Eq(2u));
+}
+
+TEST(StringPoolTest, PruneStringsWithNoReferences) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("foo");
+
+ {
+ StringPool::Ref ref_b = pool.MakeRef("wut");
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(2u));
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(2u));
+ }
+ EXPECT_THAT(pool.size(), Eq(2u));
+
+ {
+ StringPool::Ref ref_c = pool.MakeRef("bar");
+ EXPECT_THAT(pool.size(), Eq(3u));
+
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(2u));
+ }
+ EXPECT_THAT(pool.size(), Eq(2u));
+
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(1u));
+}
+
+TEST(StringPoolTest, SortAndMaintainIndexesInStringReferences) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
+
+ EXPECT_THAT(*ref_a, Eq("z"));
+ EXPECT_THAT(ref_a.index(), Eq(0u));
+
+ EXPECT_THAT(*ref_b, Eq("a"));
+ EXPECT_THAT(ref_b.index(), Eq(1u));
+
+ EXPECT_THAT(*ref_c, Eq("m"));
+ EXPECT_THAT(ref_c.index(), Eq(2u));
+
+ pool.Sort();
+
+ EXPECT_THAT(*ref_a, Eq("z"));
+ EXPECT_THAT(ref_a.index(), Eq(2u));
+
+ EXPECT_THAT(*ref_b, Eq("a"));
+ EXPECT_THAT(ref_b.index(), Eq(0u));
+
+ EXPECT_THAT(*ref_c, Eq("m"));
+ EXPECT_THAT(ref_c.index(), Eq(1u));
+}
+
+TEST(StringPoolTest, SortAndStillDedupe) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
+
+ pool.Sort();
+
+ StringPool::Ref ref_d = pool.MakeRef("z");
+ StringPool::Ref ref_e = pool.MakeRef("a");
+ StringPool::Ref ref_f = pool.MakeRef("m");
+
+ EXPECT_THAT(ref_d.index(), Eq(ref_a.index()));
+ EXPECT_THAT(ref_e.index(), Eq(ref_b.index()));
+ EXPECT_THAT(ref_f.index(), Eq(ref_c.index()));
+}
+
+TEST(StringPoolTest, AddStyles) {
+ StringPool pool;
+
+ StringPool::StyleRef ref = pool.MakeRef(StyleString{{"android"}, {Span{{"b"}, 2, 6}}});
+ EXPECT_THAT(ref.index(), Eq(0u));
+ EXPECT_THAT(ref->value, Eq("android"));
+ ASSERT_THAT(ref->spans.size(), Eq(1u));
+
+ const StringPool::Span& span = ref->spans.front();
+ EXPECT_THAT(*span.name, Eq("b"));
+ EXPECT_THAT(span.first_char, Eq(2u));
+ EXPECT_THAT(span.last_char, Eq(6u));
+}
+
+TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
+ StringPool pool;
+
+ StringPool::Ref ref = pool.MakeRef("android");
+
+ StyleString str{{"android"}, {}};
+ StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}, {}});
+
+ EXPECT_THAT(ref.index(), Ne(style_ref.index()));
+}
+
+TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) {
+ StringPool pool;
+
+ StringPool::StyleRef ref_a = pool.MakeRef(StyleString{{"beta"}, {}});
+ StringPool::Ref ref_b = pool.MakeRef("alpha");
+ StringPool::StyleRef ref_c = pool.MakeRef(StyleString{{"alpha"}, {}});
+
+ EXPECT_THAT(ref_b.index(), Ne(ref_c.index()));
+
+ pool.Sort();
+
+ EXPECT_THAT(ref_c.index(), Eq(0u));
+ EXPECT_THAT(ref_a.index(), Eq(1u));
+ EXPECT_THAT(ref_b.index(), Eq(2u));
+}
+
+TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
+ using namespace android; // For NO_ERROR on Windows.
+ NoOpDiagnostics diag;
+
+ StringPool pool;
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool, &diag);
+
+ std::unique_ptr<uint8_t[]> data = android::util::Copy(buffer);
+ ResStringPool test;
+ ASSERT_THAT(test.setTo(data.get(), buffer.size()), Eq(NO_ERROR));
+}
+
+TEST(StringPoolTest, FlattenOddCharactersUtf16) {
+ using namespace android; // For NO_ERROR on Windows.
+ NoOpDiagnostics diag;
+
+ StringPool pool;
+ pool.MakeRef("\u093f");
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf16(&buffer, pool, &diag);
+
+ std::unique_ptr<uint8_t[]> data = android::util::Copy(buffer);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ auto str = test.stringAt(0);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(str->size(), Eq(1u));
+ EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f')));
+ EXPECT_THAT(str->data()[1], Eq(0u));
+}
+
+constexpr const char* sLongString =
+ "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑"
+ "え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限"
+ "します。メール、SMSや、同期を使 "
+ "用するその他のアプリは、起動しても更新されないことがあります。バッテリーセ"
+ "ーバーは端末の充電中は自動的にOFFになります。";
+
+TEST(StringPoolTest, Flatten) {
+ using namespace android; // For NO_ERROR on Windows.
+ NoOpDiagnostics diag;
+
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("hello");
+ StringPool::Ref ref_b = pool.MakeRef("goodbye");
+ StringPool::Ref ref_c = pool.MakeRef(sLongString);
+ StringPool::Ref ref_d = pool.MakeRef("");
+ StringPool::StyleRef ref_e =
+ pool.MakeRef(StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
+
+ // Styles are always first.
+ EXPECT_THAT(ref_e.index(), Eq(0u));
+
+ EXPECT_THAT(ref_a.index(), Eq(1u));
+ EXPECT_THAT(ref_b.index(), Eq(2u));
+ EXPECT_THAT(ref_c.index(), Eq(3u));
+ EXPECT_THAT(ref_d.index(), Eq(4u));
+
+ BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
+ StringPool::FlattenUtf8(&buffers[0], pool, &diag);
+ StringPool::FlattenUtf16(&buffers[1], pool, &diag);
+
+ // Test both UTF-8 and UTF-16 buffers.
+ for (const BigBuffer& buffer : buffers) {
+ std::unique_ptr<uint8_t[]> data = android::util::Copy(buffer);
+
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+
+ EXPECT_THAT(android::util::GetString(test, 1), Eq("hello"));
+ EXPECT_THAT(android::util::GetString16(test, 1), Eq(u"hello"));
+
+ EXPECT_THAT(android::util::GetString(test, 2), Eq("goodbye"));
+ EXPECT_THAT(android::util::GetString16(test, 2), Eq(u"goodbye"));
+
+ EXPECT_THAT(android::util::GetString(test, 3), Eq(sLongString));
+ EXPECT_THAT(android::util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString)));
+
+ EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value());
+
+ EXPECT_THAT(android::util::GetString(test, 0), Eq("style"));
+ EXPECT_THAT(android::util::GetString16(test, 0), Eq(u"style"));
+
+ auto span_result = test.styleAt(0);
+ ASSERT_TRUE(span_result.has_value());
+
+ const ResStringPool_span* span = span_result->unsafe_ptr();
+ EXPECT_THAT(android::util::GetString(test, span->name.index), Eq("b"));
+ EXPECT_THAT(android::util::GetString16(test, span->name.index), Eq(u"b"));
+ EXPECT_THAT(span->firstChar, Eq(0u));
+ EXPECT_THAT(span->lastChar, Eq(1u));
+ span++;
+
+ ASSERT_THAT(span->name.index, Ne(ResStringPool_span::END));
+ EXPECT_THAT(android::util::GetString(test, span->name.index), Eq("i"));
+ EXPECT_THAT(android::util::GetString16(test, span->name.index), Eq(u"i"));
+ EXPECT_THAT(span->firstChar, Eq(2u));
+ EXPECT_THAT(span->lastChar, Eq(3u));
+ span++;
+
+ EXPECT_THAT(span->name.index, Eq(ResStringPool_span::END));
+ }
+}
+
+TEST(StringPoolTest, ModifiedUTF8) {
+ using namespace android; // For NO_ERROR on Windows.
+ NoOpDiagnostics diag;
+ StringPool pool;
+ StringPool::Ref ref_a = pool.MakeRef("\xF0\x90\x90\x80"); // 𐐀 (U+10400)
+ StringPool::Ref ref_b = pool.MakeRef("foo \xF0\x90\x90\xB7 bar"); // 𐐷 (U+10437)
+ StringPool::Ref ref_c = pool.MakeRef("\xF0\x90\x90\x80\xF0\x90\x90\xB7");
+
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool, &diag);
+ std::unique_ptr<uint8_t[]> data = android::util::Copy(buffer);
+
+ // Check that the codepoints are encoded using two three-byte surrogate pairs
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ auto str = test.string8At(0);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(*str, Eq("\xED\xA0\x81\xED\xB0\x80"));
+
+ str = test.string8At(1);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(*str, Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+
+ str = test.string8At(2);
+ ASSERT_TRUE(str.has_value());
+ EXPECT_THAT(*str, Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+
+ // Check that retrieving the strings returns the original UTF-8 character bytes
+ EXPECT_THAT(android::util::GetString(test, 0), Eq("\xF0\x90\x90\x80"));
+ EXPECT_THAT(android::util::GetString(test, 1), Eq("foo \xF0\x90\x90\xB7 bar"));
+ EXPECT_THAT(android::util::GetString(test, 2), Eq("\xF0\x90\x90\x80\xF0\x90\x90\xB7"));
+}
+
+TEST(StringPoolTest, MaxEncodingLength) {
+ NoOpDiagnostics diag;
+ using namespace android; // For NO_ERROR on Windows.
+ ResStringPool test;
+
+ StringPool pool;
+ pool.MakeRef("aaaaaaaaaa");
+ BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
+
+ // Make sure a UTF-8 string under the maximum length does not produce an error
+ EXPECT_THAT(StringPool::FlattenUtf8(&buffers[0], pool, &diag), Eq(true));
+ std::unique_ptr<uint8_t[]> data = android::util::Copy(buffers[0]);
+ test.setTo(data.get(), buffers[0].size());
+ EXPECT_THAT(android::util::GetString(test, 0), Eq("aaaaaaaaaa"));
+
+ // Make sure a UTF-16 string under the maximum length does not produce an error
+ EXPECT_THAT(StringPool::FlattenUtf16(&buffers[1], pool, &diag), Eq(true));
+ data = android::util::Copy(buffers[1]);
+ test.setTo(data.get(), buffers[1].size());
+ EXPECT_THAT(android::util::GetString16(test, 0), Eq(u"aaaaaaaaaa"));
+
+ StringPool pool2;
+ std::string longStr(50000, 'a');
+ pool2.MakeRef("this fits1");
+ pool2.MakeRef(longStr);
+ pool2.MakeRef("this fits2");
+ BigBuffer buffers2[2] = {BigBuffer(1024), BigBuffer(1024)};
+
+ // Make sure a string that exceeds the maximum length of UTF-8 produces an
+ // error and writes a shorter error string instead
+ EXPECT_THAT(StringPool::FlattenUtf8(&buffers2[0], pool2, &diag), Eq(false));
+ data = android::util::Copy(buffers2[0]);
+ test.setTo(data.get(), buffers2[0].size());
+ EXPECT_THAT(android::util::GetString(test, 0), "this fits1");
+ EXPECT_THAT(android::util::GetString(test, 1), "STRING_TOO_LARGE");
+ EXPECT_THAT(android::util::GetString(test, 2), "this fits2");
+
+ // Make sure a string that a string that exceeds the maximum length of UTF-8
+ // but not UTF-16 does not error for UTF-16
+ StringPool pool3;
+ std::u16string longStr16(50000, 'a');
+ pool3.MakeRef(longStr);
+ EXPECT_THAT(StringPool::FlattenUtf16(&buffers2[1], pool3, &diag), Eq(true));
+ data = android::util::Copy(buffers2[1]);
+ test.setTo(data.get(), buffers2[1].size());
+ EXPECT_THAT(android::util::GetString16(test, 0), Eq(longStr16));
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index d69abe5d0f11..ed30904ec179 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <algorithm>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
#include <utils/String8.h>
@@ -22,88 +23,123 @@
namespace android {
-void* createTypeData() {
- ResTable_type t;
- memset(&t, 0, sizeof(t));
+// create a ResTable_type in memory with a vector of Res_value*
+static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
+ bool compact_entry = false,
+ bool short_offsets = false)
+{
+ ResTable_type t{};
t.header.type = RES_TABLE_TYPE_TYPE;
t.header.headerSize = sizeof(t);
+ t.header.size = sizeof(t);
t.id = 1;
- t.entryCount = 3;
-
- uint32_t offsets[3];
- t.entriesStart = t.header.headerSize + sizeof(offsets);
- t.header.size = t.entriesStart;
-
- offsets[0] = 0;
- ResTable_entry e1;
- memset(&e1, 0, sizeof(e1));
- e1.size = sizeof(e1);
- e1.key.index = 0;
- t.header.size += sizeof(e1);
-
- Res_value v1;
- memset(&v1, 0, sizeof(v1));
- t.header.size += sizeof(v1);
-
- offsets[1] = ResTable_type::NO_ENTRY;
-
- offsets[2] = sizeof(e1) + sizeof(v1);
- ResTable_entry e2;
- memset(&e2, 0, sizeof(e2));
- e2.size = sizeof(e2);
- e2.key.index = 1;
- t.header.size += sizeof(e2);
-
- Res_value v2;
- memset(&v2, 0, sizeof(v2));
- t.header.size += sizeof(v2);
-
- uint8_t* data = (uint8_t*)malloc(t.header.size);
- uint8_t* p = data;
- memcpy(p, &t, sizeof(t));
- p += sizeof(t);
- memcpy(p, offsets, sizeof(offsets));
- p += sizeof(offsets);
- memcpy(p, &e1, sizeof(e1));
- p += sizeof(e1);
- memcpy(p, &v1, sizeof(v1));
- p += sizeof(v1);
- memcpy(p, &e2, sizeof(e2));
- p += sizeof(e2);
- memcpy(p, &v2, sizeof(v2));
- p += sizeof(v2);
- return data;
+ t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
+
+ t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
+ t.entriesStart = t.header.size;
+ t.entryCount = values.size();
+
+ size_t entry_size = compact_entry ? sizeof(ResTable_entry)
+ : sizeof(ResTable_entry) + sizeof(Res_value);
+ for (auto const v : values) {
+ t.header.size += v ? entry_size : 0;
+ }
+
+ uint8_t* data = (uint8_t *)malloc(t.header.size);
+ uint8_t* p_header = data;
+ uint8_t* p_offsets = data + t.header.headerSize;
+ uint8_t* p_entries = data + t.entriesStart;
+
+ memcpy(p_header, &t, sizeof(t));
+
+ size_t i = 0, entry_offset = 0;
+ uint32_t k = 0;
+ for (auto const& v : values) {
+ if (short_offsets) {
+ uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
+ *p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
+ } else {
+ uint32_t *p = reinterpret_cast<uint32_t *>(p_offsets) + i;
+ *p = v ? entry_offset : ResTable_type::NO_ENTRY;
+ }
+
+ if (v) {
+ ResTable_entry entry{};
+ if (compact_entry) {
+ entry.compact.key = i;
+ entry.compact.flags = ResTable_entry::FLAG_COMPACT | (v->dataType << 8);
+ entry.compact.data = v->data;
+ memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
+ entry_offset += sizeof(entry);
+ } else {
+ Res_value value{};
+ entry.full.size = sizeof(entry);
+ entry.full.key.index = i;
+ value = *v;
+ memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
+ memcpy(p_entries, &value, sizeof(value)); p_entries += sizeof(value);
+ entry_offset += sizeof(entry) + sizeof(value);
+ }
+ }
+ i++;
+ }
+ return reinterpret_cast<ResTable_type*>(data);
}
TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
- ResTable_type* data = (ResTable_type*) createTypeData();
+ std::vector<Res_value *> values;
- TypeVariant v(data);
+ Res_value *v1 = new Res_value{};
+ values.push_back(v1);
- TypeVariant::iterator iter = v.beginEntries();
- ASSERT_EQ(uint32_t(0), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(0), iter->key.index);
- ASSERT_NE(v.endEntries(), iter);
+ values.push_back(nullptr);
- iter++;
+ Res_value *v2 = new Res_value{};
+ values.push_back(v2);
- ASSERT_EQ(uint32_t(1), iter.index());
- ASSERT_TRUE(NULL == *iter);
- ASSERT_NE(v.endEntries(), iter);
+ Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
+ values.push_back(v3);
- iter++;
+ // test for combinations of compact_entry and short_offsets
+ for (size_t i = 0; i < 4; i++) {
+ bool compact_entry = i & 0x1, short_offsets = i & 0x2;
+ ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
+ TypeVariant v(data);
- ASSERT_EQ(uint32_t(2), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(1), iter->key.index);
- ASSERT_NE(v.endEntries(), iter);
+ TypeVariant::iterator iter = v.beginEntries();
+ ASSERT_EQ(uint32_t(0), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(0), iter->key());
+ ASSERT_NE(v.endEntries(), iter);
- iter++;
+ iter++;
- ASSERT_EQ(v.endEntries(), iter);
+ ASSERT_EQ(uint32_t(1), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
- free(data);
+ iter++;
+
+ ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(2), iter->key());
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(3), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(iter->is_compact(), compact_entry);
+ ASSERT_EQ(uint32_t(3), iter->key());
+ ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
+ ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
+
+ iter++;
+
+ ASSERT_EQ(v.endEntries(), iter);
+
+ free(data);
+ }
}
} // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 88eadccb38cf..8e847e81aa31 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/Android.bp b/libs/androidfw/tests/data/sparse/Android.bp
new file mode 100644
index 000000000000..b0da375c7971
--- /dev/null
+++ b/libs/androidfw/tests/data/sparse/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
+android_test_helper_app {
+ name: "FrameworkResourcesSparseTestApp",
+ sdk_version: "current",
+ min_sdk_version: "32",
+ aaptflags: [
+ "--enable-sparse-encoding",
+ ],
+}
+
+android_test_helper_app {
+ name: "FrameworkResourcesNotSparseTestApp",
+ sdk_version: "current",
+ min_sdk_version: "32",
+}
diff --git a/libs/androidfw/tests/data/sparse/AndroidManifest.xml b/libs/androidfw/tests/data/sparse/AndroidManifest.xml
index 27911b62447a..9c23a7227631 100644
--- a/libs/androidfw/tests/data/sparse/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/sparse/AndroidManifest.xml
@@ -17,4 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.sparse">
<application />
+ <uses-sdk android:minSdkVersion="32" />
</manifest>
diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h
index 2492dbf33f4a..a66e1af150c4 100644
--- a/libs/androidfw/tests/data/sparse/R.h
+++ b/libs/androidfw/tests/data/sparse/R.h
@@ -42,7 +42,7 @@ struct R {
struct string {
enum : uint32_t {
foo_999 = 0x7f0203e7,
- only_v26 = 0x7f0203e8
+ only_land = 0x7f0203e8
};
};
};
diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh
index 4ea5468c7df9..114ecbb7d860 100755
--- a/libs/androidfw/tests/data/sparse/gen_strings.sh
+++ b/libs/androidfw/tests/data/sparse/gen_strings.sh
@@ -1,20 +1,20 @@
#!/bin/bash
OUTPUT_default=res/values/strings.xml
-OUTPUT_v26=res/values-v26/strings.xml
+OUTPUT_land=res/values-land/strings.xml
echo "<resources>" > $OUTPUT_default
-echo "<resources>" > $OUTPUT_v26
+echo "<resources>" > $OUTPUT_land
for i in {0..999}
do
echo " <string name=\"foo_$i\">$i</string>" >> $OUTPUT_default
if [ "$(($i % 3))" -eq "0" ]
then
- echo " <string name=\"foo_$i\">$(($i * 10))</string>" >> $OUTPUT_v26
+ echo " <string name=\"foo_$i\">$(($i * 10))</string>" >> $OUTPUT_land
fi
done
echo "</resources>" >> $OUTPUT_default
-echo " <string name=\"only_v26\">only v26</string>" >> $OUTPUT_v26
-echo "</resources>" >> $OUTPUT_v26
+echo " <string name=\"only_land\">only land</string>" >> $OUTPUT_land
+echo "</resources>" >> $OUTPUT_land
diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk
index b08a621195c0..4d4d4a849033 100644
--- a/libs/androidfw/tests/data/sparse/not_sparse.apk
+++ b/libs/androidfw/tests/data/sparse/not_sparse.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-land/strings.xml
index d116087ec3c0..66222c327416 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-land/strings.xml
@@ -333,5 +333,5 @@
<string name="foo_993">9930</string>
<string name="foo_996">9960</string>
<string name="foo_999">9990</string>
- <string name="only_v26">only v26</string>
+ <string name="only_land">only land</string>
</resources>
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/values.xml b/libs/androidfw/tests/data/sparse/res/values-land/values.xml
index b396ad24aa8c..b396ad24aa8c 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/values.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-land/values.xml
diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk
index 9fd01fbf2941..0f2d75a62b96 100644
--- a/libs/androidfw/tests/data/sparse/sparse.apk
+++ b/libs/androidfw/tests/data/sparse/sparse.apk
Binary files differ
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 171e073dfce0..59e4b7acdba7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -329,6 +329,7 @@ cc_defaults {
"jni/android_util_PathParser.cpp",
"jni/Bitmap.cpp",
+ "jni/HardwareBufferHelpers.cpp",
"jni/BitmapFactory.cpp",
"jni/ByteBufferStreamAdaptor.cpp",
"jni/Camera.cpp",
@@ -340,6 +341,8 @@ cc_defaults {
"jni/Graphics.cpp",
"jni/ImageDecoder.cpp",
"jni/Interpolator.cpp",
+ "jni/MeshSpecification.cpp",
+ "jni/Mesh.cpp",
"jni/MaskFilter.cpp",
"jni/NinePatch.cpp",
"jni/NinePatchPeeker.cpp",
@@ -347,6 +350,7 @@ cc_defaults {
"jni/PaintFilter.cpp",
"jni/Path.cpp",
"jni/PathEffect.cpp",
+ "jni/PathIterator.cpp",
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
"jni/Region.cpp",
@@ -394,6 +398,7 @@ cc_defaults {
"jni/AnimatedImageDrawable.cpp",
"jni/android_graphics_TextureLayer.cpp",
"jni/android_graphics_HardwareRenderer.cpp",
+ "jni/android_graphics_HardwareBufferRenderer.cpp",
"jni/BitmapRegionDecoder.cpp",
"jni/GIFMovie.cpp",
"jni/GraphicsStatsService.cpp",
@@ -520,6 +525,7 @@ cc_defaults {
"Interpolator.cpp",
"LightingInfo.cpp",
"Matrix.cpp",
+ "MemoryPolicy.cpp",
"PathParser.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
@@ -568,6 +574,7 @@ cc_defaults {
"renderthread/VulkanSurface.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
+ "renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
@@ -587,6 +594,7 @@ cc_defaults {
"ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
+ "Tonemapper.cpp",
"TreeInfo.cpp",
"WebViewFunctorManager.cpp",
"protos/graphicsstats.proto",
@@ -634,7 +642,7 @@ cc_library_static {
cc_defaults {
name: "hwui_test_defaults",
defaults: ["hwui_defaults"],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
header_libs: ["libandroid_headers_private"],
target: {
android: {
diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/AndroidTest.xml
index 381fb9f6c7bf..75f61f5f7f9d 100644
--- a/libs/hwui/AndroidTest.xml
+++ b/libs/hwui/AndroidTest.xml
@@ -16,22 +16,23 @@
<configuration description="Config for hwuimicro">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="hwui_unit_tests->/data/nativetest/hwui_unit_tests" />
- <option name="push" value="hwuimicro->/data/benchmarktest/hwuimicro" />
- <option name="push" value="hwuimacro->/data/benchmarktest/hwuimacro" />
+ <option name="push" value="hwui_unit_tests->/data/local/tmp/nativetest/hwui_unit_tests" />
+ <option name="push" value="hwuimicro->/data/local/tmp/benchmarktest/hwuimicro" />
+ <option name="push" value="hwuimacro->/data/local/tmp/benchmarktest/hwuimacro" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
+ <option name="not-shardable" value="true" />
<test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/nativetest" />
+ <option name="native-test-device-path" value="/data/local/tmp/nativetest" />
<option name="module-name" value="hwui_unit_tests" />
</test>
<test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
- <option name="native-benchmark-device-path" value="/data/benchmarktest" />
+ <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
<option name="benchmark-module-name" value="hwuimicro" />
<option name="file-exclusion-filter-regex" value=".*\.config$" />
</test>
<test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
- <option name="native-benchmark-device-path" value="/data/benchmarktest" />
+ <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
<option name="benchmark-module-name" value="hwuimacro" />
<option name="file-exclusion-filter-regex" value=".*\.config$" />
</test>
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index d0d24a8738f4..cd4fae86aa52 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -15,19 +15,21 @@
*/
#include "CanvasTransform.h"
-#include "Properties.h"
-#include "utils/Color.h"
+#include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkGradientShader.h>
+#include <SkHighContrastFilter.h>
#include <SkPaint.h>
#include <SkShader.h>
+#include <log/log.h>
#include <algorithm>
#include <cmath>
-#include <log/log.h>
-#include <SkHighContrastFilter.h>
+#include "Properties.h"
+#include "utils/Color.h"
namespace android::uirenderer {
@@ -82,27 +84,21 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
paint.setColor(newColor);
if (paint.getShader()) {
- SkShader::GradientInfo info;
+ SkAndroidFrameworkUtils::LinearGradientInfo info;
std::array<SkColor, 10> _colorStorage;
std::array<SkScalar, _colorStorage.size()> _offsetStorage;
info.fColorCount = _colorStorage.size();
info.fColors = _colorStorage.data();
info.fColorOffsets = _offsetStorage.data();
- SkShader::GradientType type = paint.getShader()->asAGradient(&info);
-
- if (info.fColorCount <= 10) {
- switch (type) {
- case SkShader::kLinear_GradientType:
- for (int i = 0; i < info.fColorCount; i++) {
- info.fColors[i] = transformColor(transform, info.fColors[i]);
- }
- paint.setShader(SkGradientShader::MakeLinear(info.fPoint, info.fColors,
- info.fColorOffsets, info.fColorCount,
- info.fTileMode, info.fGradientFlags, nullptr));
- break;
- default:break;
- }
+ if (SkAndroidFrameworkUtils::ShaderAsALinearGradient(paint.getShader(), &info) &&
+ info.fColorCount <= _colorStorage.size()) {
+ for (int i = 0; i < info.fColorCount; i++) {
+ info.fColors[i] = transformColor(transform, info.fColors[i]);
+ }
+ paint.setShader(SkGradientShader::MakeLinear(
+ info.fPoints, info.fColors, info.fColorOffsets, info.fColorCount,
+ info.fTileMode, info.fGradientFlags, nullptr));
}
}
diff --git a/libs/hwui/CopyRequest.h b/libs/hwui/CopyRequest.h
new file mode 100644
index 000000000000..5fbd5f900716
--- /dev/null
+++ b/libs/hwui/CopyRequest.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "Rect.h"
+#include "hwui/Bitmap.h"
+
+namespace android::uirenderer {
+
+// Keep in sync with PixelCopy.java codes
+enum class CopyResult {
+ Success = 0,
+ UnknownError = 1,
+ Timeout = 2,
+ SourceEmpty = 3,
+ SourceInvalid = 4,
+ DestinationInvalid = 5,
+};
+
+struct CopyRequest {
+ Rect srcRect;
+ CopyRequest(Rect srcRect) : srcRect(srcRect) {}
+ virtual ~CopyRequest() {}
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) = 0;
+ virtual void onCopyFinished(CopyResult result) = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9a4c5505fa35..a7f8f6189a8e 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkImage.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 07594715a84c..0240c86d5f45 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -93,15 +93,21 @@ void DeviceInfo::setWideColorDataspace(ADataSpace dataspace) {
case ADATASPACE_SCRGB:
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
+ default:
+ ALOGW("Unknown dataspace %d", dataspace);
+ // Treat unknown dataspaces as sRGB, so fall through
+ [[fallthrough]];
case ADATASPACE_SRGB:
// when sRGB is returned, it means wide color gamut is not supported.
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
- default:
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
}
}
+void DeviceInfo::setSupportFp16ForHdr(bool supportFp16ForHdr) {
+ get()->mSupportFp16ForHdr = supportFp16ForHdr;
+}
+
void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) {
mVsyncPeriod = vsyncPeriod;
}
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index d5fee3f667a9..577780bbb5e0 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -16,7 +16,9 @@
#ifndef DEVICEINFO_H
#define DEVICEINFO_H
+#include <SkColorSpace.h>
#include <SkImageInfo.h>
+#include <SkRefCnt.h>
#include <android/data_space.h>
#include <mutex>
@@ -57,6 +59,9 @@ public:
}
static void setWideColorDataspace(ADataSpace dataspace);
+ static void setSupportFp16ForHdr(bool supportFp16ForHdr);
+ static bool isSupportFp16ForHdr() { return get()->mSupportFp16ForHdr; };
+
// this value is only valid after the GPU has been initialized and there is a valid graphics
// context or if you are using the HWUI_NULL_GPU
int maxTextureSize() const;
@@ -86,6 +91,7 @@ private:
int mMaxTextureSize;
sk_sp<SkColorSpace> mWideColorSpace = SkColorSpace::MakeSRGB();
+ bool mSupportFp16ForHdr = false;
SkColorType mWideColorType = SkColorType::kN32_SkColorType;
int mDisplaysSize = 0;
int mPhysicalDisplayIndex = -1;
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 4ec782f6fec0..e2127efca716 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -52,3 +52,4 @@ X(DrawShadowRec)
X(DrawVectorDrawable)
X(DrawRippleDrawable)
X(DrawWebView)
+X(DrawMesh)
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f53a54..b15b6cb9a9ec 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@ public:
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
+ set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
return *this;
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index c24cabb287de..b7e99994355c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -22,8 +22,11 @@
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GrDirectContext.h>
+#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
#include <gui/TraceUtils.h>
#include <utils/GLUtils.h>
#include <utils/NdkUtils.h>
@@ -39,6 +42,8 @@
namespace android::uirenderer {
+static constexpr auto kThreadTimeout = 60000_ms;
+
class AHBUploader;
// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
// interface.
@@ -77,7 +82,7 @@ public:
}
void postIdleTimeoutCheck() {
- mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
+ mUploadThread->queue().postDelayed(kThreadTimeout, [this]() { this->idleTimeoutCheck(); });
}
protected:
@@ -94,7 +99,7 @@ private:
bool shouldTimeOutLocked() {
nsecs_t durationSince = systemTime() - mLastUpload;
- return durationSince > 2000_ms;
+ return durationSince > kThreadTimeout;
}
void idleTimeoutCheck() {
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 81057a24c29c..00ee99648889 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -17,6 +17,9 @@
#pragma once
#include <hwui/Bitmap.h>
+#include <SkRefCnt.h>
+
+class SkBitmap;
namespace android::uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9053c1240957..fc3118ae32dd 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include <SkBlendMode.h>
+
#include <log/log.h>
namespace android {
diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp
new file mode 100644
index 000000000000..ca1312e75f4c
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "MemoryPolicy.h"
+
+#include <android-base/properties.h>
+
+#include <optional>
+#include <string_view>
+
+#include "Properties.h"
+
+namespace android::uirenderer {
+
+constexpr static MemoryPolicy sDefaultMemoryPolicy;
+constexpr static MemoryPolicy sPersistentOrSystemPolicy{
+ .contextTimeout = 10_s,
+ .useAlternativeUiHidden = true,
+};
+constexpr static MemoryPolicy sLowRamPolicy{
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+};
+constexpr static MemoryPolicy sExtremeLowRam{
+ .initialMaxSurfaceAreaScale = 0.2f,
+ .surfaceSizeMultiplier = 5 * 4.0f,
+ .backgroundRetentionPercent = 0.2f,
+ .contextTimeout = 5_s,
+ .minimumResourceRetention = 1_s,
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+ .releaseContextOnStoppedOnly = true,
+};
+
+const MemoryPolicy& loadMemoryPolicy() {
+ if (Properties::isSystemOrPersistent) {
+ return sPersistentOrSystemPolicy;
+ }
+ std::string memoryPolicy = base::GetProperty(PROPERTY_MEMORY_POLICY, "");
+ if (memoryPolicy == "default") {
+ return sDefaultMemoryPolicy;
+ }
+ if (memoryPolicy == "lowram") {
+ return sLowRamPolicy;
+ }
+ if (memoryPolicy == "extremelowram") {
+ return sExtremeLowRam;
+ }
+
+ if (Properties::isLowRam) {
+ return sLowRamPolicy;
+ }
+ return sDefaultMemoryPolicy;
+}
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
new file mode 100644
index 000000000000..41ced8cebf83
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.h
@@ -0,0 +1,62 @@
+/*
+ * 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 "utils/TimeUtils.h"
+
+namespace android::uirenderer {
+
+// Values mirror those from ComponentCallbacks2.java
+enum class TrimLevel {
+ COMPLETE = 80,
+ MODERATE = 60,
+ BACKGROUND = 40,
+ UI_HIDDEN = 20,
+ RUNNING_CRITICAL = 15,
+ RUNNING_LOW = 10,
+ RUNNING_MODERATE = 5,
+};
+
+struct MemoryPolicy {
+ // The initial scale factor applied to the display resolution. The default is 1, but
+ // lower values may be used to start with a smaller initial cache size. The cache will
+ // be adjusted if larger frames are actually rendered
+ float initialMaxSurfaceAreaScale = 1.0f;
+ // The foreground cache size multiplier. The surface area of the screen will be multiplied
+ // by this
+ float surfaceSizeMultiplier = 12.0f * 4.0f;
+ // How much of the foreground cache size should be preserved when going into the background
+ float backgroundRetentionPercent = 0.5f;
+ // How long after the last renderer goes away before the GPU context is released. A value
+ // of 0 means only drop the context on background TRIM signals
+ nsecs_t contextTimeout = 10_s;
+ // 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;
+ // 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;
+ // Whether or not to only purge scratch resources when triggering UI Hidden or background
+ // collection
+ bool purgeScratchOnly = true;
+ // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
+ bool releaseContextOnStoppedOnly = false;
+};
+
+const MemoryPolicy& loadMemoryPolicy();
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5a67eb9935dd..6affc6a81685 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -82,11 +82,15 @@ bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
float Properties::defaultSdrWhitePoint = 200.f;
-bool Properties::useHintManager = true;
+bool Properties::useHintManager = false;
int Properties::targetCpuTimePercentage = 70;
bool Properties::enableWebViewOverlays = true;
+bool Properties::isHighEndGfx = true;
+bool Properties::isLowRam = false;
+bool Properties::isSystemOrPersistent = false;
+
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
@@ -138,7 +142,7 @@ bool Properties::load() {
runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
- useHintManager = base::GetBoolProperty(PROPERTY_USE_HINT_MANAGER, true);
+ useHintManager = base::GetBoolProperty(PROPERTY_USE_HINT_MANAGER, false);
targetCpuTimePercentage = base::GetIntProperty(PROPERTY_TARGET_CPU_TIME_PERCENTAGE, 70);
if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f8c67903a8b..96a517629eaa 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -193,6 +193,8 @@ enum DebugLevel {
*/
#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -292,16 +294,27 @@ public:
static bool enableWebViewOverlays;
+ static bool isHighEndGfx;
+ static bool isLowRam;
+ static bool isSystemOrPersistent;
+
static StretchEffectBehavior getStretchEffectBehavior() {
return stretchEffectBehavior;
}
static void setIsHighEndGfx(bool isHighEndGfx) {
+ Properties::isHighEndGfx = isHighEndGfx;
stretchEffectBehavior = isHighEndGfx ?
StretchEffectBehavior::ShaderHWUI :
StretchEffectBehavior::UniformScale;
}
+ static void setIsLowRam(bool isLowRam) { Properties::isLowRam = isLowRam; }
+
+ static void setIsSystemOrPersistent(bool isSystemOrPersistent) {
+ Properties::isSystemOrPersistent = isSystemOrPersistent;
+ }
+
/**
* Used for testing. Typical configuration of stretch behavior is done
* through setIsHighEndGfx
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index a3ba88e4ee8a..8dcd6dbe6421 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -16,12 +16,27 @@
#include "Readback.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColorSpace.h>
+#include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkSurface.h>
+#include <gui/TraceUtils.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <shaders/shaders.h>
#include <sync/sync.h>
#include <system/window.h>
-#include <gui/TraceUtils.h>
#include "DeferredLayerUpdater.h"
#include "Properties.h"
+#include "Tonemapper.h"
#include "hwui/Bitmap.h"
#include "pipeline/skia/LayerDrawable.h"
#include "renderthread/EglManager.h"
@@ -37,8 +52,7 @@ namespace uirenderer {
#define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)
-CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
- SkBitmap* bitmap) {
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
ATRACE_CALL();
// Setup the source
AHardwareBuffer* rawSourceBuffer;
@@ -51,44 +65,57 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
// Really this shouldn't ever happen, but better safe than sorry.
if (err == UNKNOWN_TRANSACTION) {
ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
- return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
windowTransform);
if (err != NO_ERROR) {
ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
if (rawSourceBuffer == nullptr) {
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
+ return request->onCopyFinished(CopyResult::SourceEmpty);
}
UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
AHardwareBuffer_Desc description;
AHardwareBuffer_describe(sourceBuffer.get(), &description);
if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
+ {
+ ATRACE_NAME("sync_wait");
+ if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return request->onCopyFinished(CopyResult::Timeout);
+ }
}
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+ int32_t dataspace = ANativeWindow_getBuffersDataSpace(window);
+
+ // If the application is not updating the Surface themselves, e.g., another
+ // process is producing buffers for the application to display, then
+ // ANativeWindow_getBuffersDataSpace will return an unknown answer, so grab
+ // the dataspace from buffer metadata instead, if it exists.
+ if (dataspace == 0) {
+ dataspace = AHardwareBuffer_getDataSpace(sourceBuffer.get());
+ }
+
+ sk_sp<SkColorSpace> colorSpace =
+ DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
sk_sp<SkImage> image =
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
if (!image.get()) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
sk_sp<GrDirectContext> grContext = mRenderThread.requireGrContext();
- SkRect srcRect = inSrcRect.toSkRect();
+ SkRect srcRect = request->srcRect.toSkRect();
SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
SkISize imageWH = SkISize::Make(description.width, description.height);
@@ -136,10 +163,12 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
SK_RECT_ARGS(textureRect));
if (!srcRect.intersect(textureRect)) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
+ SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
+ SkBitmap* bitmap = &skBitmap;
sk_sp<SkSurface> tmpSurface =
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
@@ -152,7 +181,7 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
@@ -211,6 +240,10 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
const bool hasBufferCrop = cropRect.left < cropRect.right && cropRect.top < cropRect.bottom;
auto constraint =
hasBufferCrop ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint;
+
+ static constexpr float kMaxLuminanceNits = 4000.f;
+ tonemapPaint(image->imageInfo(), canvas->imageInfo(), kMaxLuminanceNits, paint);
+
canvas->drawImageRect(image, imageSrcRect, imageDstRect, sampling, &paint, constraint);
canvas->restore();
@@ -223,52 +256,13 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
!tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
!tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
ALOGW("Unable to convert content into the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
bitmap->notifyPixelsChanged();
- return CopyResult::Success;
-}
-
-CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
- SkBitmap* bitmap) {
- // Setup the source
- AHardwareBuffer* rawSourceBuffer;
- int rawSourceFence;
- Matrix4 texTransform;
- status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
- texTransform.data);
- base::unique_fd sourceFence(rawSourceFence);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (rawSourceBuffer == nullptr) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
-
- UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
- AHardwareBuffer_Desc description;
- AHardwareBuffer_describe(sourceBuffer.get(), &description);
- if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
-
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
- sk_sp<SkImage> image =
- SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
- return copyImageInto(image, srcRect, bitmap);
+ return request->onCopyFinished(CopyResult::Success);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -306,14 +300,14 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
SkBitmap* bitmap) {
ATRACE_CALL();
+ if (!image.get()) {
+ return CopyResult::UnknownError;
+ }
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
} else {
mRenderThread.requireVkContext();
}
- if (!image.get()) {
- return CopyResult::UnknownError;
- }
int imgWidth = image->width();
int imgHeight = image->height();
sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d0d748ff5c16..a092d472abf0 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,11 +16,16 @@
#pragma once
+#include <SkRefCnt.h>
+
+#include "CopyRequest.h"
#include "Matrix.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
-#include <SkBitmap.h>
+class SkBitmap;
+class SkImage;
+struct SkRect;
namespace android {
class Bitmap;
@@ -31,23 +36,13 @@ namespace uirenderer {
class DeferredLayerUpdater;
class Layer;
-// Keep in sync with PixelCopy.java codes
-enum class CopyResult {
- Success = 0,
- UnknownError = 1,
- Timeout = 2,
- SourceEmpty = 3,
- SourceInvalid = 4,
- DestinationInvalid = 5,
-};
-
class Readback {
public:
explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
+ void copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request);
CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);
@@ -55,7 +50,6 @@ public:
CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
private:
- CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index a285462eef74..3f21940d35a7 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -15,13 +15,16 @@
*/
#include "RecordingCanvas.h"
-#include <hwui/Paint.h>
#include <GrRecordingContext.h>
+#include <SkMesh.h>
+#include <hwui/Paint.h>
#include <experimental/type_traits>
+#include <utility>
#include "SkAndroidFrameworkUtils.h"
+#include "SkBlendMode.h"
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
#include "SkColor.h"
@@ -29,10 +32,14 @@
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
#include "SkImageFilter.h"
+#include "SkImageInfo.h"
#include "SkLatticeIter.h"
#include "SkMath.h"
+#include "SkPaint.h"
#include "SkPicture.h"
+#include "SkRRect.h"
#include "SkRSXform.h"
+#include "SkRect.h"
#include "SkRegion.h"
#include "SkTextBlob.h"
#include "SkVertices.h"
@@ -266,7 +273,6 @@ struct DrawDRRect final : Op {
SkPaint paint;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawDRRect(outer, inner, paint); }
};
-
struct DrawAnnotation final : Op {
static const auto kType = Type::DrawAnnotation;
DrawAnnotation(const SkRect& rect, SkData* value) : rect(rect), value(sk_ref_sp(value)) {}
@@ -448,6 +454,16 @@ struct DrawVertices final : Op {
c->drawVertices(vertices, mode, paint);
}
};
+struct DrawMesh final : Op {
+ static const auto kType = Type::DrawMesh;
+ DrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
+ : mesh(mesh), blender(std::move(blender)), paint(paint) {}
+
+ SkMesh mesh;
+ sk_sp<SkBlender> blender;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh, blender, paint); }
+};
struct DrawAtlas final : Op {
static const auto kType = Type::DrawAtlas;
DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling,
@@ -759,6 +775,10 @@ void DisplayListData::drawPoints(SkCanvas::PointMode mode, size_t count, const S
void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, const SkPaint& paint) {
this->push<DrawVertices>(0, vert, mode, paint);
}
+void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp<SkBlender>& blender,
+ const SkPaint& paint) {
+ this->push<DrawMesh>(0, mesh, blender, paint);
+}
void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
const SkColor colors[], int count, SkBlendMode xfermode,
const SkSamplingOptions& sampling, const SkRect* cull,
@@ -1101,6 +1121,10 @@ void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices,
SkBlendMode mode, const SkPaint& paint) {
fDL->drawVertices(vertices, mode, paint);
}
+void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
+ const SkPaint& paint) {
+ fDL->drawMesh(mesh, blender, paint);
+}
void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[],
const SkRect texs[], const SkColor colors[], int count,
SkBlendMode bmode, const SkSamplingOptions& sampling,
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 212b4e72dcb2..2539694a73ee 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -34,6 +34,9 @@
#include <SkRuntimeEffect.h>
#include <vector>
+enum class SkBlendMode;
+class SkRRect;
+
namespace android {
namespace uirenderer {
@@ -109,6 +112,8 @@ private:
void drawRRect(const SkRRect&, const SkPaint&);
void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
+ void drawMesh(const SkMesh&, const sk_sp<SkBlender>&, const SkPaint&);
+
void drawAnnotation(const SkRect&, const char*, SkData*);
void drawDrawable(SkDrawable*, const SkMatrix*);
void drawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
@@ -208,6 +213,7 @@ public:
const SkPaint&) override;
void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
+ void onDrawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) override;
void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 24443c8c9836..7170226037f9 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -71,9 +71,14 @@ public:
, right(rect.fRight)
, bottom(rect.fBottom) {}
- friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); }
+ friend int operator==(const Rect& a, const Rect& b) {
+ return a.left == b.left &&
+ a.top == b.top &&
+ a.right == b.right &&
+ a.bottom == b.bottom;
+ }
- friend int operator!=(const Rect& a, const Rect& b) { return memcmp(&a, &b, sizeof(a)); }
+ friend int operator!=(const Rect& a, const Rect& b) { return !(a == b); }
inline void clear() { left = top = right = bottom = 0.0f; }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index da0476259b97..bdc48e91f6cb 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -16,7 +16,6 @@
#pragma once
-#include <SkCamera.h>
#include <SkMatrix.h>
#include <utils/LinearAllocator.h>
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index f9b3a8c12b2e..d83d78f650aa 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -18,6 +18,7 @@
#include "CanvasProperty.h"
#include "NinePatchUtils.h"
+#include "SkBlendMode.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
@@ -27,6 +28,7 @@
#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
+#include <SkBitmap.h>
#include <SkCanvasPriv.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
@@ -36,8 +38,13 @@
#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImagePriv.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
#include <SkPicture.h>
#include <SkRSXform.h>
+#include <SkRRect.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
#include <SkShader.h>
#include <SkTemplates.h>
#include <SkTextBlob.h>
@@ -245,10 +252,11 @@ const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
-void SkiaCanvas::punchHole(const SkRRect& rect) {
+void SkiaCanvas::punchHole(const SkRRect& rect, float alpha) {
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mCanvas->drawRRect(rect, paint);
}
@@ -562,6 +570,10 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); });
}
+void SkiaCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
+ mCanvas->drawMesh(mesh, blender, paint);
+}
+
// ----------------------------------------------------------------------------
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 715007cdcd3b..31e3b4c3c7e2 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -33,6 +33,9 @@
#include <cassert>
#include <optional>
+enum class SkBlendMode;
+class SkRRect;
+
namespace android {
// Holds an SkCanvas reference plus additional native data.
@@ -63,7 +66,7 @@ public:
LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void setBitmap(const SkBitmap& bitmap) override;
@@ -117,8 +120,8 @@ public:
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const Paint& paint) override;
- virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
- const Paint& paint) override;
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const Paint& paint) override;
virtual void drawCircle(float x, float y, float radius, const Paint& paint) override;
virtual void drawOval(float left, float top, float right, float bottom,
@@ -127,6 +130,8 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) override;
virtual void drawPath(const SkPath& path, const Paint& paint) override;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override;
+ virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
+ const SkPaint& paint) override;
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index 0695dd1ab218..b28b15aa6cf4 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -17,9 +17,10 @@
#include "SkiaInterpolator.h"
#include "include/core/SkMath.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkTypes.h"
#include "include/private/SkFixed.h"
#include "include/private/SkMalloc.h"
-#include "include/private/SkTo.h"
#include "src/core/SkTSearch.h"
typedef int Dot14;
@@ -91,7 +92,6 @@ static float SkUnitCubicInterp(float value, float bx, float by, float cx, float
SkiaInterpolatorBase::SkiaInterpolatorBase() {
fStorage = nullptr;
fTimes = nullptr;
- SkDEBUGCODE(fTimesArray = nullptr;)
}
SkiaInterpolatorBase::~SkiaInterpolatorBase() {
@@ -102,14 +102,13 @@ SkiaInterpolatorBase::~SkiaInterpolatorBase() {
void SkiaInterpolatorBase::reset(int elemCount, int frameCount) {
fFlags = 0;
- fElemCount = SkToU8(elemCount);
- fFrameCount = SkToS16(frameCount);
+ fElemCount = static_cast<uint8_t>(elemCount);
+ fFrameCount = static_cast<int16_t>(frameCount);
fRepeat = SK_Scalar1;
if (fStorage) {
sk_free(fStorage);
fStorage = nullptr;
fTimes = nullptr;
- SkDEBUGCODE(fTimesArray = nullptr);
}
}
@@ -205,7 +204,6 @@ SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T
SkiaInterpolator::SkiaInterpolator() {
INHERITED::reset(0, 0);
fValues = nullptr;
- SkDEBUGCODE(fScalarsArray = nullptr;)
}
SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) {
@@ -218,10 +216,6 @@ void SkiaInterpolator::reset(int elemCount, int frameCount) {
fStorage = sk_malloc_throw((sizeof(float) * elemCount + sizeof(SkTimeCode)) * frameCount);
fTimes = (SkTimeCode*)fStorage;
fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount);
-#ifdef SK_DEBUG
- fTimesArray = (SkTimeCode(*)[10])fTimes;
- fScalarsArray = (float(*)[10])fValues;
-#endif
}
#define SK_Fixed1Third (SK_Fixed1 / 3)
diff --git a/libs/hwui/SkiaInterpolator.h b/libs/hwui/SkiaInterpolator.h
index c03f502528be..9422cb526a8f 100644
--- a/libs/hwui/SkiaInterpolator.h
+++ b/libs/hwui/SkiaInterpolator.h
@@ -17,7 +17,8 @@
#ifndef SkiaInterpolator_DEFINED
#define SkiaInterpolator_DEFINED
-#include "include/private/SkTo.h"
+#include <cstddef>
+#include <cstdint>
class SkiaInterpolatorBase {
public:
@@ -46,7 +47,9 @@ public:
@param mirror If true, the odd repeats interpolate from the last key
frame and the first.
*/
- void setMirror(bool mirror) { fFlags = SkToU8((fFlags & ~kMirror) | (int)mirror); }
+ void setMirror(bool mirror) {
+ fFlags = static_cast<uint8_t>((fFlags & ~kMirror) | (int)mirror);
+ }
/** Set the repeat count. The repeat count may be fractional.
@param repeatCount Multiplies the total time by this scalar.
@@ -57,7 +60,7 @@ public:
@param reset If true, the odd repeats interpolate from the last key
frame and the first.
*/
- void setReset(bool reset) { fFlags = SkToU8((fFlags & ~kReset) | (int)reset); }
+ void setReset(bool reset) { fFlags = static_cast<uint8_t>((fFlags & ~kReset) | (int)reset); }
Result timeToT(uint32_t time, float* T, int* index, bool* exact) const;
@@ -75,9 +78,6 @@ protected:
};
SkTimeCode* fTimes; // pointer into fStorage
void* fStorage;
-#ifdef SK_DEBUG
- SkTimeCode (*fTimesArray)[10];
-#endif
};
class SkiaInterpolator : public SkiaInterpolatorBase {
diff --git a/libs/hwui/TEST_MAPPING b/libs/hwui/TEST_MAPPING
index b1719a979ce5..03682e82e28d 100644
--- a/libs/hwui/TEST_MAPPING
+++ b/libs/hwui/TEST_MAPPING
@@ -5,6 +5,9 @@
},
{
"name": "CtsAccelerationTestCases"
+ },
+ {
+ "name": "hwui_unit_tests"
}
],
"imports": [
diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp
new file mode 100644
index 000000000000..a7e76b631140
--- /dev/null
+++ b/libs/hwui/Tonemapper.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 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 "Tonemapper.h"
+
+#include <SkRuntimeEffect.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
+
+#include "utils/Color.h"
+
+namespace android::uirenderer {
+
+namespace {
+
+class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder {
+public:
+ explicit ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
+ : SkRuntimeEffectBuilder(std::move(effect)) {}
+
+ sk_sp<SkColorFilter> makeColorFilter() {
+ return this->effect()->makeColorFilter(this->uniforms());
+ }
+};
+
+static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearEffect& linearEffect,
+ float maxDisplayLuminance,
+ float currentDisplayLuminanceNits,
+ float maxLuminance) {
+ auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
+ auto [runtimeEffect, error] = SkRuntimeEffect::MakeForColorFilter(std::move(shaderString));
+ if (!runtimeEffect) {
+ LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+ }
+
+ ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
+
+ const auto uniforms =
+ shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance,
+ currentDisplayLuminanceNits, maxLuminance);
+
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
+ }
+
+ return effectBuilder.makeColorFilter();
+}
+
+static bool extractTransfer(ui::Dataspace dataspace) {
+ return dataspace & HAL_DATASPACE_TRANSFER_MASK;
+}
+
+static bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = extractTransfer(dataspace);
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+static ui::Dataspace getDataspace(const SkImageInfo& image) {
+ return static_cast<ui::Dataspace>(
+ ColorSpaceToADataSpace(image.colorSpace(), image.colorType()));
+}
+
+} // namespace
+
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+ SkPaint& paint) {
+ const auto sourceDataspace = getDataspace(source);
+ const auto destinationDataspace = getDataspace(destination);
+
+ if (extractTransfer(sourceDataspace) != extractTransfer(destinationDataspace) &&
+ (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace))) {
+ const auto effect = shaders::LinearEffect{
+ .inputDataspace = sourceDataspace,
+ .outputDataspace = destinationDataspace,
+ .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType,
+ .fakeInputDataspace = destinationDataspace,
+ .type = shaders::LinearEffect::SkSLType::ColorFilter};
+ constexpr float kMaxDisplayBrightnessNits = 1000.f;
+ constexpr float kCurrentDisplayBrightnessNits = 500.f;
+ sk_sp<SkColorFilter> colorFilter = createLinearEffectColorFilter(
+ effect, kMaxDisplayBrightnessNits, kCurrentDisplayBrightnessNits, maxLuminanceNits);
+
+ if (paint.getColorFilter()) {
+ paint.setColorFilter(SkColorFilters::Compose(paint.refColorFilter(), colorFilter));
+ } else {
+ paint.setColorFilter(colorFilter);
+ }
+ }
+}
+
+} // namespace android::uirenderer
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java b/libs/hwui/Tonemapper.h
index 9c82eea1e8b8..c0d5325fa9f8 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java
+++ b/libs/hwui/Tonemapper.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 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.
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+#pragma once
-import android.app.Activity;
-import android.os.Bundle;
+#include <SkCanvas.h>
-public class SplitScreenActivity extends Activity {
+namespace android::uirenderer {
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.activity_splitscreen);
- }
-}
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+ SkPaint& paint);
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 983c7766273a..536ff781badc 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -21,9 +21,10 @@
#include <utils/Log.h>
#include "PathParser.h"
-#include "SkColorFilter.h"
+#include "SkImage.h"
#include "SkImageInfo.h"
-#include "SkShader.h"
+#include "SkSamplingOptions.h"
+#include "SkScalar.h"
#include "hwui/Paint.h"
#ifdef __ANDROID__
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 30bb04ae8361..c92654c479c1 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -31,6 +31,7 @@
#include <SkPath.h>
#include <SkPathMeasure.h>
#include <SkRect.h>
+#include <SkRefCnt.h>
#include <SkShader.h>
#include <SkSurface.h>
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 942c0506321c..b7a15633ff6d 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -53,6 +53,7 @@ extern int register_android_graphics_FontFamily(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
extern int register_android_graphics_Paint(JNIEnv* env);
extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathIterator(JNIEnv* env);
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
@@ -100,6 +101,7 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
{"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
{"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
+ {"android.graphics.PathIterator", REG_JNI(register_android_graphics_PathIterator)},
{"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
{"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index bc6bc456ba5a..c442a7b1d17c 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -24,6 +24,11 @@
#include <GraphicsJNI.h>
#include <hwui/Bitmap.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
+#include <SkStream.h>
#include <utils/Color.h>
using namespace android;
diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp
index 2a939efed9bb..905b123076a2 100644
--- a/libs/hwui/apex/android_canvas.cpp
+++ b/libs/hwui/apex/android_canvas.cpp
@@ -23,7 +23,9 @@
#include <utils/Color.h>
#include <SkBitmap.h>
+#include <SkColorSpace.h>
#include <SkSurface.h>
+#include <SkRefCnt.h>
using namespace android;
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
index 70bd085343ce..cc79cba5e19c 100644
--- a/libs/hwui/apex/android_paint.cpp
+++ b/libs/hwui/apex/android_paint.cpp
@@ -19,6 +19,7 @@
#include "TypeCast.h"
#include <hwui/Paint.h>
+#include <SkBlendMode.h>
using namespace android;
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index e1f5abd786bf..f57d80c496ad 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -59,6 +59,7 @@ extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
extern int register_android_graphics_Paint(JNIEnv* env);
extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathIterator(JNIEnv* env);
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv*);
extern int register_android_graphics_Region(JNIEnv* env);
@@ -75,11 +76,14 @@ extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
extern int register_android_graphics_text_TextShaper(JNIEnv *env);
+extern int register_android_graphics_MeshSpecification(JNIEnv* env);
+extern int register_android_graphics_Mesh(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env);
#ifdef NDEBUG
#define REG_JNI(name) { name }
@@ -94,59 +98,64 @@ extern int register_android_view_ThreadedRenderer(JNIEnv* env);
};
#endif
-static const RegJNIRec gRegJNI[] = {
- REG_JNI(register_android_graphics_Canvas),
- // This needs to be before register_android_graphics_Graphics, or the latter
- // will not be able to find the jmethodID for ColorSpace.get().
- REG_JNI(register_android_graphics_ColorSpace),
- REG_JNI(register_android_graphics_Graphics),
- REG_JNI(register_android_graphics_Bitmap),
- REG_JNI(register_android_graphics_BitmapFactory),
- REG_JNI(register_android_graphics_BitmapRegionDecoder),
- REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
- REG_JNI(register_android_graphics_Camera),
- REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
- REG_JNI(register_android_graphics_CanvasProperty),
- REG_JNI(register_android_graphics_ColorFilter),
- REG_JNI(register_android_graphics_DrawFilter),
- REG_JNI(register_android_graphics_FontFamily),
- REG_JNI(register_android_graphics_HardwareRendererObserver),
- REG_JNI(register_android_graphics_ImageDecoder),
- REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
- REG_JNI(register_android_graphics_Interpolator),
- REG_JNI(register_android_graphics_MaskFilter),
- REG_JNI(register_android_graphics_Matrix),
- REG_JNI(register_android_graphics_Movie),
- REG_JNI(register_android_graphics_NinePatch),
- REG_JNI(register_android_graphics_Paint),
- REG_JNI(register_android_graphics_Path),
- REG_JNI(register_android_graphics_PathMeasure),
- REG_JNI(register_android_graphics_PathEffect),
- REG_JNI(register_android_graphics_Picture),
- REG_JNI(register_android_graphics_Region),
- REG_JNI(register_android_graphics_Shader),
- REG_JNI(register_android_graphics_RenderEffect),
- REG_JNI(register_android_graphics_TextureLayer),
- REG_JNI(register_android_graphics_Typeface),
- REG_JNI(register_android_graphics_YuvImage),
- REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
- REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
- REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
- REG_JNI(register_android_graphics_drawable_VectorDrawable),
- REG_JNI(register_android_graphics_fonts_Font),
- REG_JNI(register_android_graphics_fonts_FontFamily),
- REG_JNI(register_android_graphics_pdf_PdfDocument),
- REG_JNI(register_android_graphics_pdf_PdfEditor),
- REG_JNI(register_android_graphics_pdf_PdfRenderer),
- REG_JNI(register_android_graphics_text_MeasuredText),
- REG_JNI(register_android_graphics_text_LineBreaker),
- REG_JNI(register_android_graphics_text_TextShaper),
-
- REG_JNI(register_android_util_PathParser),
- REG_JNI(register_android_view_RenderNode),
- REG_JNI(register_android_view_DisplayListCanvas),
- REG_JNI(register_android_view_ThreadedRenderer),
-};
+ static const RegJNIRec gRegJNI[] = {
+ REG_JNI(register_android_graphics_Canvas),
+ // This needs to be before register_android_graphics_Graphics, or the latter
+ // will not be able to find the jmethodID for ColorSpace.get().
+ REG_JNI(register_android_graphics_ColorSpace),
+ REG_JNI(register_android_graphics_Graphics),
+ REG_JNI(register_android_graphics_Bitmap),
+ REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_BitmapRegionDecoder),
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
+ REG_JNI(register_android_graphics_Camera),
+ REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
+ REG_JNI(register_android_graphics_CanvasProperty),
+ REG_JNI(register_android_graphics_ColorFilter),
+ REG_JNI(register_android_graphics_DrawFilter),
+ REG_JNI(register_android_graphics_FontFamily),
+ REG_JNI(register_android_graphics_HardwareRendererObserver),
+ REG_JNI(register_android_graphics_ImageDecoder),
+ REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
+ REG_JNI(register_android_graphics_Interpolator),
+ REG_JNI(register_android_graphics_MaskFilter),
+ REG_JNI(register_android_graphics_Matrix),
+ REG_JNI(register_android_graphics_Movie),
+ REG_JNI(register_android_graphics_NinePatch),
+ REG_JNI(register_android_graphics_Paint),
+ REG_JNI(register_android_graphics_Path),
+ REG_JNI(register_android_graphics_PathIterator),
+ REG_JNI(register_android_graphics_PathMeasure),
+ REG_JNI(register_android_graphics_PathEffect),
+ REG_JNI(register_android_graphics_Picture),
+ REG_JNI(register_android_graphics_Region),
+ REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_RenderEffect),
+ REG_JNI(register_android_graphics_TextureLayer),
+ REG_JNI(register_android_graphics_Typeface),
+ REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
+ REG_JNI(register_android_graphics_drawable_VectorDrawable),
+ REG_JNI(register_android_graphics_fonts_Font),
+ REG_JNI(register_android_graphics_fonts_FontFamily),
+ REG_JNI(register_android_graphics_pdf_PdfDocument),
+ REG_JNI(register_android_graphics_pdf_PdfEditor),
+ REG_JNI(register_android_graphics_pdf_PdfRenderer),
+ REG_JNI(register_android_graphics_text_MeasuredText),
+ REG_JNI(register_android_graphics_text_LineBreaker),
+ REG_JNI(register_android_graphics_text_TextShaper),
+ REG_JNI(register_android_graphics_MeshSpecification),
+ REG_JNI(register_android_graphics_Mesh),
+
+ REG_JNI(register_android_util_PathParser),
+ REG_JNI(register_android_view_RenderNode),
+ REG_JNI(register_android_view_DisplayListCanvas),
+ REG_JNI(register_android_graphics_HardwareBufferRenderer),
+
+ REG_JNI(register_android_view_ThreadedRenderer),
+ };
} // namespace android
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index fdc97a4fd8ba..2dcbca8273e7 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -17,13 +17,19 @@
#pragma once
#include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
#include <SkCanvas.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <SkVertices.h>
+#include <SkClipOp.h>
#include <SkImage.h>
+#include <SkPaint.h>
+#include <SkPath.h>
#include <SkPicture.h>
+#include <SkRRect.h>
+#include <SkRect.h>
+#include <SkRegion.h>
#include <SkRuntimeEffect.h>
+#include <SkSamplingOptions.h>
+#include <SkVertices.h>
#include <log/log.h>
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 67f47580a70f..feafc2372442 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -35,9 +35,15 @@
#endif
#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkEncodedImageFormat.h>
+#include <SkHighContrastFilter.h>
+#include <SkImageEncoder.h>
#include <SkImagePriv.h>
+#include <SkPixmap.h>
+#include <SkRect.h>
+#include <SkStream.h>
#include <SkWebpEncoder.h>
-#include <SkHighContrastFilter.h>
#include <limits>
namespace android {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 94a047c06ced..133f1fe0a1e7 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -19,9 +19,9 @@
#include <SkColorFilter.h>
#include <SkColorSpace.h>
#include <SkImage.h>
-#include <SkImage.h>
#include <SkImageInfo.h>
#include <SkPixelRef.h>
+#include <SkRefCnt.h>
#include <cutils/compiler.h>
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
#include <android/hardware_buffer.h>
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 270d24af99fd..8b52551fc107 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -15,6 +15,8 @@
*/
#include "BlurDrawLooper.h"
+#include <SkBlurTypes.h>
+#include <SkColorSpace.h>
#include <SkMaskFilter.h>
namespace android {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index b046f45d9c57..cd8af3d933b1 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -26,6 +26,7 @@
#include "hwui/PaintFilter.h"
#include <SkFontMetrics.h>
+#include <SkRRect.h>
namespace android {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 82777646f3a2..2a2019199bda 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -30,7 +30,9 @@
#include <SkMatrix.h>
class SkAnimatedImage;
+enum class SkBlendMode;
class SkCanvasState;
+class SkRRect;
class SkRuntimeShaderBuilder;
class SkVertices;
@@ -151,7 +153,7 @@ public:
LOG_ALWAYS_FATAL("Not supported");
}
- virtual void punchHole(const SkRRect& rect) = 0;
+ virtual void punchHole(const SkRRect& rect, float alpha) = 0;
// ----------------------------------------------------------------------------
// Canvas state operations
@@ -225,6 +227,7 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) = 0;
virtual void drawPath(const SkPath& path, const Paint& paint) = 0;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0;
+ virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint& paint) = 0;
// Bitmap-based
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0;
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index cef2233fc371..b6d73b39d8d0 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -17,9 +17,11 @@
#include <SkAndroidCodec.h>
#include <SkCodec.h>
+#include <SkColorSpace.h>
#include <SkImageInfo.h>
#include <SkPngChunkReader.h>
#include <SkRect.h>
+#include <SkRefCnt.h>
#include <SkSize.h>
#include <cutils/compiler.h>
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 2db3ace1cd43..34cb4aef70d9 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -16,10 +16,13 @@
#include "MinikinSkia.h"
-#include <SkFontDescriptor.h>
#include <SkFont.h>
+#include <SkFontDescriptor.h>
#include <SkFontMetrics.h>
#include <SkFontMgr.h>
+#include <SkRect.h>
+#include <SkScalar.h>
+#include <SkStream.h>
#include <SkTypeface.h>
#include <log/log.h>
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 5a9d2508230e..3c67edc9a428 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -125,9 +125,14 @@ Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) {
}
Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::FontFamily>>&& families,
- int weight, int italic) {
+ int weight, int italic, const Typeface* fallback) {
Typeface* result = new Typeface;
- result->fFontCollection.reset(new minikin::FontCollection(families));
+ if (fallback == nullptr) {
+ result->fFontCollection = minikin::FontCollection::create(std::move(families));
+ } else {
+ result->fFontCollection =
+ fallback->fFontCollection->createCollectionWithFamilies(std::move(families));
+ }
if (weight == RESOLVE_BY_FONT_TABLE || italic == RESOLVE_BY_FONT_TABLE) {
int weightFromFont;
@@ -191,8 +196,8 @@ void Typeface::setRobotoTypefaceForTest() {
std::vector<std::shared_ptr<minikin::Font>> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
- std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
- std::make_shared<minikin::FontFamily>(std::move(fonts)));
+ std::shared_ptr<minikin::FontCollection> collection =
+ minikin::FontCollection::create(minikin::FontFamily::create(std::move(fonts)));
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 0c3ef01ab26b..565136e53676 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -78,7 +78,8 @@ public:
Typeface* src, const std::vector<minikin::FontVariation>& variations);
static Typeface* createFromFamilies(
- std::vector<std::shared_ptr<minikin::FontFamily>>&& families, int weight, int italic);
+ std::vector<std::shared_ptr<minikin::FontFamily>>&& families, int weight, int italic,
+ const Typeface* fallback);
static void setDefault(const Typeface* face);
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index c40b858268be..373e893b9a25 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -21,8 +21,11 @@
#include <SkAndroidCodec.h>
#include <SkAnimatedImage.h>
#include <SkColorFilter.h>
+#include <SkEncodedImageFormat.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/ImageDecoder.h>
#include <hwui/Canvas.h>
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 5db0783cf83e..540abec38ebc 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -2,40 +2,41 @@
#define LOG_TAG "Bitmap"
#include "Bitmap.h"
+#include <hwui/Bitmap.h>
+#include <hwui/Paint.h>
+
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "HardwareBufferHelpers.h"
#include "SkBitmap.h"
+#include "SkBlendMode.h"
#include "SkCanvas.h"
#include "SkColor.h"
#include "SkColorSpace.h"
-#include "SkPixelRef.h"
-#include "SkImageEncoder.h"
+#include "SkData.h"
#include "SkImageInfo.h"
-#include "GraphicsJNI.h"
+#include "SkPaint.h"
+#include "SkPixmap.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
#include "SkStream.h"
-#include "SkWebpEncoder.h"
-
+#include "SkTypes.h"
#include "android_nio_utils.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-#include <hwui/Paint.h>
-#include <hwui/Bitmap.h>
-#include <utils/Color.h>
#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
#include <android-base/unique_fd.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <android/binder_parcel_platform.h>
-#include <android/binder_parcel_utils.h>
-#include <private/android/AHardwareBufferHelpers.h>
#include <cutils/ashmem.h>
-#include <dlfcn.h>
#include <renderthread/RenderProxy.h>
#include <sys/mman.h>
#endif
#include <inttypes.h>
#include <string.h>
+
#include <memory>
-#include <string>
#define DEBUG_PARCEL 0
@@ -1189,18 +1190,11 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit
return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
}
-#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
-typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject);
-AHB_from_HB AHardwareBuffer_fromHardwareBuffer;
-
-typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*);
-AHB_to_HB AHardwareBuffer_toHardwareBuffer;
-#endif
-
static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
jlong colorSpacePtr) {
#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
- AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
+ AHardwareBuffer* buffer = uirenderer::HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(
+ env, hardwareBuffer);
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
GraphicsJNI::getNativeColorSpace(colorSpacePtr));
if (!bitmap.get()) {
@@ -1223,7 +1217,8 @@ static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) {
}
Bitmap& bitmap = bitmapHandle->bitmap();
- return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer());
+ return uirenderer::HardwareBufferHelpers::AHardwareBuffer_toHardwareBuffer(
+ env, bitmap.hardwareBuffer());
#else
return nullptr;
#endif
@@ -1321,18 +1316,7 @@ int register_android_graphics_Bitmap(JNIEnv* env)
gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
-
-#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- AHardwareBuffer_fromHardwareBuffer =
- (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer");
- LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr,
- "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!");
-
- AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer");
- LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr,
- " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!");
-#endif
+ uirenderer::HardwareBufferHelpers::init();
return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
NELEM(gBitmapMethods));
}
diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h
index 73eca3aa8ef8..21a93f066d9b 100644
--- a/libs/hwui/jni/Bitmap.h
+++ b/libs/hwui/jni/Bitmap.h
@@ -19,7 +19,6 @@
#include <jni.h>
#include <android/bitmap.h>
-class SkBitmap;
struct SkImageInfo;
namespace android {
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 4e9daa4b0c16..320d3322904f 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -8,9 +8,19 @@
#include "MimeType.h"
#include "NinePatchPeeker.h"
#include "SkAndroidCodec.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
#include "SkCanvas.h"
+#include "SkColorSpace.h"
+#include "SkEncodedImageFormat.h"
+#include "SkImageInfo.h"
#include "SkMath.h"
+#include "SkPaint.h"
#include "SkPixelRef.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkSamplingOptions.h"
+#include "SkSize.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkUtils.h"
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 1c20415dcc8f..eb56ae310231 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -25,6 +25,7 @@
#include "BitmapRegionDecoder.h"
#include "SkBitmap.h"
#include "SkCodec.h"
+#include "SkColorSpace.h"
#include "SkData.h"
#include "SkStream.h"
diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
index b10540cb3fbd..97dbc9ac171f 100644
--- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
+++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
@@ -2,6 +2,7 @@
#include "GraphicsJNI.h"
#include "Utils.h"
+#include <SkData.h>
#include <SkStream.h>
using namespace android;
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index cef21f91f3c1..4bd7ef47b871 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -17,6 +17,7 @@
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index ce5ac382aeff..28e71d74e5b9 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -24,6 +24,7 @@
#include "SkData.h"
#include "SkFontMgr.h"
#include "SkRefCnt.h"
+#include "SkStream.h"
#include "SkTypeface.h"
#include "Utils.h"
#include "fonts/Font.h"
@@ -84,9 +85,9 @@ static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) {
if (builder->fonts.empty()) {
return 0;
}
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
+ std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
builder->langId, builder->variant, std::move(builder->fonts),
- true /* isCustomFallback */);
+ true /* isCustomFallback */, false /* isDefaultFallback */);
if (family->getCoverage().length() == 0) {
return 0;
}
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index fef51b8d2f79..ae6ac4ce4ecc 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -7,9 +7,11 @@
#include "Movie.h"
+#include "SkBitmap.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
+#include "SkTypes.h"
#include "gif_lib.h"
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 33669ac0a34e..c8358497ad62 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -8,10 +8,18 @@
#include <nativehelper/JNIHelp.h>
#include "GraphicsJNI.h"
+#include "include/private/SkTemplates.h" // SkTAddOffset
+#include "SkBitmap.h"
#include "SkCanvas.h"
+#include "SkColorSpace.h"
#include "SkFontMetrics.h"
+#include "SkImageInfo.h"
#include "SkMath.h"
+#include "SkPixelRef.h"
+#include "SkPoint.h"
+#include "SkRect.h"
#include "SkRegion.h"
+#include "SkTypes.h"
#include <cutils/ashmem.h>
#include <hwui/Canvas.h>
@@ -568,14 +576,22 @@ jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
skcms_TransferFunction transferParams;
- // We can only handle numerical transfer functions at the moment
- LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
-
- jobject params = env->NewObject(gTransferParameters_class,
- gTransferParameters_constructorMethodID,
- transferParams.a, transferParams.b, transferParams.c,
- transferParams.d, transferParams.e, transferParams.f,
- transferParams.g);
+ decodeColorSpace->transferFn(&transferParams);
+ auto res = skcms_TransferFunction_getType(&transferParams);
+ LOG_ALWAYS_FATAL_IF(res == skcms_TFType_HLGinvish || res == skcms_TFType_Invalid);
+
+ jobject params;
+ if (res == skcms_TFType_PQish || res == skcms_TFType_HLGish) {
+ params = env->NewObject(gTransferParameters_class, gTransferParameters_constructorMethodID,
+ transferParams.a, transferParams.b, transferParams.c,
+ transferParams.d, transferParams.e, transferParams.f,
+ transferParams.g, true);
+ } else {
+ params = env->NewObject(gTransferParameters_class, gTransferParameters_constructorMethodID,
+ transferParams.a, transferParams.b, transferParams.c,
+ transferParams.d, transferParams.e, transferParams.f,
+ transferParams.g, false);
+ }
jfloatArray xyzArray = env->NewFloatArray(9);
jfloat xyz[9] = {
@@ -800,8 +816,8 @@ int register_android_graphics_Graphics(JNIEnv* env)
gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
"android/graphics/ColorSpace$Rgb$TransferParameters"));
- gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
- "<init>", "(DDDDDDD)V");
+ gTransferParameters_constructorMethodID =
+ GetMethodIDOrDie(env, gTransferParameters_class, "<init>", "(DDDDDDDZ)V");
gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 085a905abaf8..c4a61ccd1e5f 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -2,19 +2,18 @@
#define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
#include <cutils/compiler.h>
+#include <hwui/Bitmap.h>
+#include <hwui/Canvas.h>
-#include "Bitmap.h"
#include "BRDAllocator.h"
+#include "Bitmap.h"
#include "SkBitmap.h"
#include "SkCodec.h"
-#include "SkPixelRef.h"
+#include "SkColorSpace.h"
#include "SkMallocPixelRef.h"
+#include "SkPixelRef.h"
#include "SkPoint.h"
#include "SkRect.h"
-#include "SkColorSpace.h"
-#include <hwui/Canvas.h>
-#include <hwui/Bitmap.h>
-
#include "graphics_jni_helpers.h"
class SkCanvas;
@@ -335,6 +334,26 @@ private:
int fLen;
};
+class JGlobalRefHolder {
+public:
+ JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
+
+ virtual ~JGlobalRefHolder() {
+ GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject);
+ mObject = nullptr;
+ }
+
+ jobject object() { return mObject; }
+ JavaVM* vm() { return mVm; }
+
+private:
+ JGlobalRefHolder(const JGlobalRefHolder&) = delete;
+ void operator=(const JGlobalRefHolder&) = delete;
+
+ JavaVM* mVm;
+ jobject mObject;
+};
+
void doThrowNPE(JNIEnv* env);
void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception
void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument
diff --git a/libs/hwui/jni/HardwareBufferHelpers.cpp b/libs/hwui/jni/HardwareBufferHelpers.cpp
new file mode 100644
index 000000000000..7e3f771b6b3d
--- /dev/null
+++ b/libs/hwui/jni/HardwareBufferHelpers.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "HardwareBufferHelpers.h"
+
+#include <dlfcn.h>
+#include <log/log.h>
+
+#ifdef __ANDROID__
+typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject);
+typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*);
+static AHB_from_HB fromHardwareBuffer = nullptr;
+static AHB_to_HB toHardwareBuffer = nullptr;
+#endif
+
+void android::uirenderer::HardwareBufferHelpers::init() {
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ fromHardwareBuffer = (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer");
+ LOG_ALWAYS_FATAL_IF(fromHardwareBuffer == nullptr,
+ "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!");
+
+ toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer");
+ LOG_ALWAYS_FATAL_IF(toHardwareBuffer == nullptr,
+ " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!");
+#endif
+}
+
+AHardwareBuffer* android::uirenderer::HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(
+ JNIEnv* env, jobject hardwarebuffer) {
+#ifdef __ANDROID__
+ LOG_ALWAYS_FATAL_IF(fromHardwareBuffer == nullptr,
+ "Failed to find symbol AHardwareBuffer_fromHardwareBuffer, did you forget "
+ "to call HardwareBufferHelpers::init?");
+ return fromHardwareBuffer(env, hardwarebuffer);
+#else
+ ALOGE("ERROR attempting to invoke AHardwareBuffer_fromHardwareBuffer on non Android "
+ "configuration");
+ return nullptr;
+#endif
+}
+
+jobject android::uirenderer::HardwareBufferHelpers::AHardwareBuffer_toHardwareBuffer(
+ JNIEnv* env, AHardwareBuffer* ahardwarebuffer) {
+#ifdef __ANDROID__
+ LOG_ALWAYS_FATAL_IF(toHardwareBuffer == nullptr,
+ "Failed to find symbol AHardwareBuffer_toHardwareBuffer, did you forget to "
+ "call HardwareBufferHelpers::init?");
+ return toHardwareBuffer(env, ahardwarebuffer);
+#else
+ ALOGE("ERROR attempting to invoke AHardwareBuffer_toHardwareBuffer on non Android "
+ "configuration");
+ return nullptr;
+#endif
+} \ No newline at end of file
diff --git a/libs/hwui/jni/HardwareBufferHelpers.h b/libs/hwui/jni/HardwareBufferHelpers.h
new file mode 100644
index 000000000000..326babfb0b34
--- /dev/null
+++ b/libs/hwui/jni/HardwareBufferHelpers.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#ifndef HARDWAREBUFFER_JNI_HELPERS_H
+#define HARDWAREBUFFER_JNI_HELPERS_H
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+namespace android {
+namespace uirenderer {
+
+class HardwareBufferHelpers {
+public:
+ static void init();
+ static AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv*, jobject);
+ static jobject AHardwareBuffer_toHardwareBuffer(JNIEnv*, AHardwareBuffer*);
+
+private:
+ HardwareBufferHelpers() = default; // not to be instantiated
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // HARDWAREBUFFER_JNI_HELPERS_H \ No newline at end of file
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index f7b8c014be6e..bad710dec274 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -29,8 +29,12 @@
#include <FrontBufferedStream.h>
#include <SkAndroidCodec.h>
-#include <SkEncodedImageFormat.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkImageInfo.h>
+#include <SkRect.h>
#include <SkStream.h>
+#include <SkString.h>
#include <androidfw/Asset.h>
#include <fcntl.h>
diff --git a/libs/hwui/jni/JvmErrorReporter.h b/libs/hwui/jni/JvmErrorReporter.h
new file mode 100644
index 000000000000..5e10b9d93275
--- /dev/null
+++ b/libs/hwui/jni/JvmErrorReporter.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#ifndef JVMERRORREPORTER_H
+#define JVMERRORREPORTER_H
+
+#include <TreeInfo.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "GraphicsJNI.h"
+
+namespace android {
+namespace uirenderer {
+
+class JvmErrorReporter : public android::uirenderer::ErrorHandler {
+public:
+ JvmErrorReporter(JNIEnv* env) { env->GetJavaVM(&mVm); }
+
+ virtual void onError(const std::string& message) override {
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
+ jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
+ }
+
+private:
+ JavaVM* mVm;
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // JVMERRORREPORTER_H
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 5383032e0f77..048ce025ce27 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -2,6 +2,7 @@
#include "SkMaskFilter.h"
#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
+#include "SkBlurTypes.h"
#include "SkTableMaskFilter.h"
static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
diff --git a/libs/hwui/jni/Mesh.cpp b/libs/hwui/jni/Mesh.cpp
new file mode 100644
index 000000000000..3aac48dd08b1
--- /dev/null
+++ b/libs/hwui/jni/Mesh.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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 <GLES/gl.h>
+#include <Mesh.h>
+#include <SkMesh.h>
+
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+namespace android {
+
+sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size,
+ jboolean isDirect) {
+ auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
+ auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size);
+ return vertexBuffer;
+}
+
+sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size,
+ jboolean isDirect) {
+ auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
+ auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size);
+ return indexBuffer;
+}
+
+static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
+ jboolean isDirect, jint vertexCount, jint vertexOffset, jint left, jint top,
+ jint right, jint bottom) {
+ auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
+ sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
+ genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect);
+ auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
+ auto meshResult = SkMesh::Make(
+ skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
+ SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
+
+ if (!meshResult.error.isEmpty()) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
+ }
+
+ auto meshPtr = std::make_unique<MeshWrapper>(
+ MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
+ return reinterpret_cast<jlong>(meshPtr.release());
+}
+
+static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
+ jboolean isVertexDirect, jint vertexCount, jint vertexOffset,
+ jobject indexBuffer, jboolean isIndexDirect, jint indexCount,
+ jint indexOffset, jint left, jint top, jint right, jint bottom) {
+ auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
+ sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
+ genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect);
+ sk_sp<SkMesh::IndexBuffer> skIndexBuffer =
+ genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect);
+ auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
+
+ auto meshResult = SkMesh::MakeIndexed(
+ skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
+ skIndexBuffer, indexCount, indexOffset,
+ SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
+
+ if (!meshResult.error.isEmpty()) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
+ }
+ auto meshPtr = std::make_unique<MeshWrapper>(
+ MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
+ return reinterpret_cast<jlong>(meshPtr.release());
+}
+
+static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) {
+ auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
+ auto mesh = wrapper->mesh;
+ if (indexed) {
+ wrapper->mesh = SkMesh::MakeIndexed(sk_ref_sp(mesh.spec()), mesh.mode(),
+ sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(),
+ mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()),
+ mesh.indexCount(), mesh.indexOffset(),
+ wrapper->builder.fUniforms, mesh.bounds())
+ .mesh;
+ } else {
+ wrapper->mesh = SkMesh::Make(sk_ref_sp(mesh.spec()), mesh.mode(),
+ sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(),
+ mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds())
+ .mesh;
+ }
+}
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
+ const char* uniformName, const float values[], int count,
+ bool isColor) {
+ MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jfloat value1, jfloat value2, jfloat value3, jfloat value4,
+ jint count) {
+ auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false);
+}
+
+static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
+ jfloatArray jvalues, jboolean isColor) {
+ auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+ nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
+ autoValues.length(), isColor);
+}
+
+static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
+ const char* uniformName, const int values[], int count) {
+ MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jint value1, jint value2, jint value3, jint value4, jint count) {
+ auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count);
+}
+
+static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
+ jintArray values) {
+ auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
+ ScopedUtfChars name(env, uniformName);
+ AutoJavaIntArray autoValues(env, values, 0);
+ nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
+ autoValues.length());
+}
+
+static void MeshWrapper_destroy(MeshWrapper* wrapper) {
+ delete wrapper;
+}
+
+static jlong getMeshFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
+}
+
+static const JNINativeMethod gMeshMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
+ {"nativeMake", "(JILjava/nio/Buffer;ZIIIIII)J", (void*)make},
+ {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIIIII)J",
+ (void*)makeIndexed},
+ {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
+
+int register_android_graphics_Mesh(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h
new file mode 100644
index 000000000000..7a73f2d5c470
--- /dev/null
+++ b/libs/hwui/jni/Mesh.h
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
+#define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
+
+#include <SkMesh.h>
+#include <jni.h>
+
+#include <utility>
+
+#include "graphics_jni_helpers.h"
+
+#define gIndexByteSize 2
+
+// A smart pointer that provides read only access to Java.nio.Buffer. This handles both
+// direct and indrect buffers, allowing access to the underlying data in both
+// situations. If passed a null buffer, we will throw NullPointerException,
+// and c_data will return nullptr.
+//
+// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void *
+// conversion.
+class ScopedJavaNioBuffer {
+public:
+ ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, jint size, jboolean isDirect)
+ : mEnv(env), mBuffer(buffer) {
+ if (buffer == nullptr) {
+ mDataBase = nullptr;
+ mData = nullptr;
+ jniThrowNullPointerException(env);
+ } else {
+ mArray = (jarray) nullptr;
+ if (isDirect) {
+ mData = getDirectBufferPointer(mEnv, mBuffer);
+ } else {
+ mData = setIndirectData(size);
+ }
+ }
+ }
+
+ ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); }
+
+ ~ScopedJavaNioBuffer() { reset(); }
+
+ void reset() {
+ if (mDataBase) {
+ releasePointer(mEnv, mArray, mDataBase, JNI_FALSE);
+ mDataBase = nullptr;
+ }
+ }
+
+ ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept {
+ if (this != &rhs) {
+ reset();
+
+ mEnv = rhs.mEnv;
+ mBuffer = rhs.mBuffer;
+ mDataBase = rhs.mDataBase;
+ mData = rhs.mData;
+ mArray = rhs.mArray;
+ rhs.mEnv = nullptr;
+ rhs.mData = nullptr;
+ rhs.mBuffer = nullptr;
+ rhs.mArray = nullptr;
+ rhs.mDataBase = nullptr;
+ }
+ return *this;
+ }
+
+ const void* data() const { return mData; }
+
+private:
+ /**
+ * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data
+ * from a java.nio.Buffer.
+ */
+ void* getDirectBufferPointer(JNIEnv* env, jobject buffer) {
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+ pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
+ if (pointer == 0) {
+ jniThrowException(mEnv, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
+ return nullptr;
+ }
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) {
+ env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
+ }
+
+ static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining,
+ jint* offset) {
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+
+ jlong pointer;
+ pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
+ *remaining = (limit - position) << elementSizeShift;
+ if (pointer != 0L) {
+ *array = nullptr;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ *array = jniGetNioBufferBaseArray(env, buffer);
+ *offset = jniGetNioBufferBaseArrayOffset(env, buffer);
+ return nullptr;
+ }
+
+ /**
+ * This is a copy of
+ * static void android_glBufferData__IILjava_nio_Buffer_2I
+ * from com_google_android_gles_jni_GLImpl.cpp
+ */
+ void* setIndirectData(jint size) {
+ jint exception;
+ const char* exceptionType;
+ const char* exceptionMessage;
+ jint bufferOffset = (jint)0;
+ jint remaining;
+ void* tempData;
+
+ if (mBuffer) {
+ tempData =
+ (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset);
+ if (remaining < size) {
+ exception = 1;
+ exceptionType = "java/lang/IllegalArgumentException";
+ exceptionMessage = "remaining() < size < needed";
+ goto exit;
+ }
+ }
+ if (mBuffer && tempData == nullptr) {
+ mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0);
+ tempData = (void*)(mDataBase + bufferOffset);
+ }
+ return tempData;
+ exit:
+ if (mArray) {
+ releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE);
+ }
+ if (exception) {
+ jniThrowException(mEnv, exceptionType, exceptionMessage);
+ }
+ return nullptr;
+ }
+
+ JNIEnv* mEnv;
+
+ // Java Buffer data
+ void* mData;
+ jobject mBuffer;
+
+ // Indirect Buffer Data
+ jarray mArray;
+ char* mDataBase;
+};
+
+class MeshUniformBuilder {
+public:
+ struct MeshUniform {
+ template <typename T>
+ std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
+ const T& val) {
+ if (!fVar) {
+ SkDEBUGFAIL("Assigning to missing variable");
+ } else if (sizeof(val) != fVar->sizeInBytes()) {
+ SkDEBUGFAIL("Incorrect value size");
+ } else {
+ memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), &val,
+ szeof(val));
+ }
+ }
+
+ MeshUniform& operator=(const SkMatrix& val) {
+ if (!fVar) {
+ SkDEBUGFAIL("Assigning to missing variable");
+ } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
+ SkDEBUGFAIL("Incorrect value size");
+ } else {
+ float* data =
+ SkTAddOffset<float>(fOwner->writableUniformData(), (ptrdiff_t)fVar->offset);
+ data[0] = val.get(0);
+ data[1] = val.get(3);
+ data[2] = val.get(6);
+ data[3] = val.get(1);
+ data[4] = val.get(4);
+ data[5] = val.get(7);
+ data[6] = val.get(2);
+ data[7] = val.get(5);
+ data[8] = val.get(8);
+ }
+ return *this;
+ }
+
+ template <typename T>
+ bool set(const T val[], const int count) {
+ static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
+ if (!fVar) {
+ SkDEBUGFAIL("Assigning to missing variable");
+ return false;
+ } else if (sizeof(T) * count != fVar->sizeInBytes()) {
+ SkDEBUGFAIL("Incorrect value size");
+ return false;
+ } else {
+ memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), val,
+ sizeof(T) * count);
+ }
+ return true;
+ }
+
+ MeshUniformBuilder* fOwner;
+ const SkRuntimeEffect::Uniform* fVar;
+ };
+ MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; }
+
+ explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) {
+ fMeshSpec = sk_sp(meshSpec);
+ fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
+ }
+
+ sk_sp<SkData> fUniforms;
+
+private:
+ void* writableUniformData() {
+ if (!fUniforms->unique()) {
+ fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
+ }
+ return fUniforms->writable_data();
+ }
+
+ sk_sp<SkMeshSpecification> fMeshSpec;
+};
+
+struct MeshWrapper {
+ SkMesh mesh;
+ MeshUniformBuilder builder;
+};
+#endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
diff --git a/libs/hwui/jni/MeshSpecification.cpp b/libs/hwui/jni/MeshSpecification.cpp
new file mode 100644
index 000000000000..ae9792df3d82
--- /dev/null
+++ b/libs/hwui/jni/MeshSpecification.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 <SkMesh.h>
+
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+namespace android {
+
+using Attribute = SkMeshSpecification::Attribute;
+using Varying = SkMeshSpecification::Varying;
+
+static struct {
+ jclass clazz{};
+ jfieldID type{};
+ jfieldID offset{};
+ jfieldID name{};
+} gAttributeInfo;
+
+static struct {
+ jclass clazz{};
+ jfieldID type{};
+ jfieldID name{};
+} gVaryingInfo;
+
+std::vector<Attribute> extractAttributes(JNIEnv* env, jobjectArray attributes) {
+ int size = env->GetArrayLength(attributes);
+ std::vector<Attribute> attVector;
+ attVector.reserve(size);
+ for (int i = 0; i < size; i++) {
+ jobject attribute = env->GetObjectArrayElement(attributes, i);
+ auto name = (jstring)env->GetObjectField(attribute, gAttributeInfo.name);
+ auto attName = ScopedUtfChars(env, name);
+ Attribute temp{Attribute::Type(env->GetIntField(attribute, gAttributeInfo.type)),
+ static_cast<size_t>(env->GetIntField(attribute, gAttributeInfo.offset)),
+ SkString(attName.c_str())};
+ attVector.push_back(std::move(temp));
+ }
+ return attVector;
+}
+
+std::vector<Varying> extractVaryings(JNIEnv* env, jobjectArray varyings) {
+ int size = env->GetArrayLength(varyings);
+ std::vector<Varying> varyVector;
+ varyVector.reserve(size);
+ for (int i = 0; i < size; i++) {
+ jobject varying = env->GetObjectArrayElement(varyings, i);
+ auto name = (jstring)env->GetObjectField(varying, gVaryingInfo.name);
+ auto varyName = ScopedUtfChars(env, name);
+ Varying temp{Varying::Type(env->GetIntField(varying, gVaryingInfo.type)),
+ SkString(varyName.c_str())};
+ varyVector.push_back(std::move(temp));
+ }
+
+ return varyVector;
+}
+
+static jlong Make(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
+ jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpecResult = SkMeshSpecification::Make(attributes, vertexStride, varyings,
+ SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()));
+ if (meshSpecResult.specification.get() == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", meshSpecResult.error.c_str());
+ }
+
+ return reinterpret_cast<jlong>(meshSpecResult.specification.release());
+}
+
+static jlong MakeWithCS(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
+ jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader,
+ jlong colorSpace) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpecResult = SkMeshSpecification::Make(
+ attributes, vertexStride, varyings, SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()), GraphicsJNI::getNativeColorSpace(colorSpace));
+
+ if (meshSpecResult.specification.get() == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", meshSpecResult.error.c_str());
+ }
+
+ return reinterpret_cast<jlong>(meshSpecResult.specification.release());
+}
+
+static jlong MakeWithAlpha(JNIEnv* env, jobject thiz, jobjectArray attributeArray,
+ jint vertexStride, jobjectArray varyingArray, jstring vertexShader,
+ jstring fragmentShader, jlong colorSpace, jint alphaType) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpecResult = SkMeshSpecification::Make(
+ attributes, vertexStride, varyings, SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()), GraphicsJNI::getNativeColorSpace(colorSpace),
+ SkAlphaType(alphaType));
+
+ if (meshSpecResult.specification.get() == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", meshSpecResult.error.c_str());
+ }
+
+ return reinterpret_cast<jlong>(meshSpecResult.specification.release());
+}
+
+static void MeshSpecification_safeUnref(SkMeshSpecification* meshSpec) {
+ SkSafeUnref(meshSpec);
+}
+
+static jlong getMeshSpecificationFinalizer() {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshSpecification_safeUnref));
+}
+
+static const JNINativeMethod gMeshSpecificationMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getMeshSpecificationFinalizer},
+ {"nativeMake",
+ "([Landroid/graphics/MeshSpecification$Attribute;I[Landroid/graphics/"
+ "MeshSpecification$Varying;"
+ "Ljava/lang/String;Ljava/lang/String;)J",
+ (void*)Make},
+ {"nativeMakeWithCS",
+ "([Landroid/graphics/MeshSpecification$Attribute;I"
+ "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;J)J",
+ (void*)MakeWithCS},
+ {"nativeMakeWithAlpha",
+ "([Landroid/graphics/MeshSpecification$Attribute;I"
+ "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;JI)J",
+ (void*)MakeWithAlpha}};
+
+int register_android_graphics_MeshSpecification(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/MeshSpecification",
+ gMeshSpecificationMethods, NELEM(gMeshSpecificationMethods));
+
+ gAttributeInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Attribute");
+ gAttributeInfo.type = env->GetFieldID(gAttributeInfo.clazz, "mType", "I");
+ gAttributeInfo.offset = env->GetFieldID(gAttributeInfo.clazz, "mOffset", "I");
+ gAttributeInfo.name = env->GetFieldID(gAttributeInfo.clazz, "mName", "Ljava/lang/String;");
+
+ gVaryingInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Varying");
+ gVaryingInfo.type = env->GetFieldID(gVaryingInfo.clazz, "mType", "I");
+ gVaryingInfo.name = env->GetFieldID(gVaryingInfo.clazz, "mName", "Ljava/lang/String;");
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h
index 736890d5215e..02113dd58ec8 100644
--- a/libs/hwui/jni/Movie.h
+++ b/libs/hwui/jni/Movie.h
@@ -13,6 +13,7 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkRefCnt.h"
+#include "SkTypes.h"
class SkStreamRewindable;
diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp
index ae9e04e617b0..abb75fa99c94 100644
--- a/libs/hwui/jni/MovieImpl.cpp
+++ b/libs/hwui/jni/MovieImpl.cpp
@@ -5,11 +5,12 @@
* found in the LICENSE file.
*/
#include "Movie.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include "SkTypes.h"
// We should never see this in normal operation since our time values are
-// 0-based. So we use it as a sentinal.
+// 0-based. So we use it as a sentinel.
#define UNINITIALIZED_MSEC ((SkMSec)-1)
Movie::Movie()
@@ -81,8 +82,6 @@ const SkBitmap& Movie::bitmap()
////////////////////////////////////////////////////////////////////
-#include "SkStream.h"
-
Movie* Movie::DecodeMemory(const void* data, size_t length) {
SkMemoryStream stream(data, length, false);
return Movie::DecodeStream(&stream);
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 08fc80fbdafd..d50a8a22b5cb 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -24,8 +24,10 @@
#include <hwui/Paint.h>
#include <utils/Log.h>
+#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkLatticeIter.h"
+#include "SkRect.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
#include "NinePatchPeeker.h"
diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp
index 9171fc687276..d85ede5dc6d2 100644
--- a/libs/hwui/jni/NinePatchPeeker.cpp
+++ b/libs/hwui/jni/NinePatchPeeker.cpp
@@ -16,7 +16,7 @@
#include "NinePatchPeeker.h"
-#include <SkBitmap.h>
+#include <SkScalar.h>
#include <cutils/compiler.h>
using namespace android;
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index f76863255153..b563627edad9 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -26,12 +26,14 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include "SkColorFilter.h"
+#include "SkColorSpace.h"
#include "SkFont.h"
#include "SkFontMetrics.h"
#include "SkFontTypes.h"
#include "SkMaskFilter.h"
#include "SkPath.h"
#include "SkPathEffect.h"
+#include "SkPathUtils.h"
#include "SkShader.h"
#include "SkBlendMode.h"
#include "unicode/uloc.h"
@@ -496,17 +498,32 @@ namespace PaintGlue {
return true;
}
- static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
- jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
+ 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) {
+ if (advances) {
+ size_t advancesLength = env->GetArrayLength(advances);
+ if ((size_t)(count + advancesIndex) > advancesLength) {
+ doThrowAIOOBE(env);
+ return 0;
+ }
+ }
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
- if (offset == start + count) {
+ if (offset == start + count && advances == nullptr) {
return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
bufSize, nullptr);
}
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
advancesArray.get());
- return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
+
+ float result = minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
+ if (advances) {
+ minikin::distributeAdvances(advancesArray.get(), buf, start, count);
+ env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
+ }
+ return result;
}
static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text,
@@ -514,9 +531,23 @@ namespace PaintGlue {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
ScopedCharArrayRO textArray(env, text);
- jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
- start - contextStart, end - start, contextEnd - contextStart, isRtl,
- offset - contextStart);
+ jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart,
+ isRtl, offset - contextStart, nullptr, 0);
+ return result;
+ }
+
+ static jfloat getRunCharacterAdvance___CIIIIZI_FI_F(JNIEnv* env, jclass, jlong paintHandle,
+ jcharArray text, jint start, jint end,
+ jint contextStart, jint contextEnd,
+ jboolean isRtl, jint offset,
+ jfloatArray advances, jint advancesIndex) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ ScopedCharArrayRO textArray(env, text);
+ jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart,
+ isRtl, offset - contextStart, advances, advancesIndex);
return result;
}
@@ -779,7 +810,7 @@ namespace PaintGlue {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
- return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+ return skpathutils::FillPathWithPaint(*src, *obj, dst) ? JNI_TRUE : JNI_FALSE;
}
static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
@@ -1033,113 +1064,112 @@ namespace PaintGlue {
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
- {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
- {"nInit","()J", (void*) PaintGlue::init},
- {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
- {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC},
- {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
- {"nGetTextAdvances","(J[CIIIII[FI)F",
- (void*) PaintGlue::getTextAdvances___CIIIII_FI},
- {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F",
- (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
-
- {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
- {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
- (void*) PaintGlue::getTextRunCursor__String},
- {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
- {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
- {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
- (void*) PaintGlue::getStringBounds },
- {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
- (void*) PaintGlue::getCharArrayBounds },
- {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
- {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
- {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
- (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
- {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
- (void*)PaintGlue::getFontMetricsIntForText___C},
- {"nGetFontMetricsIntForText",
- "(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
- (void*)PaintGlue::getFontMetricsIntForText___String},
-
- // --------------- @FastNative ----------------------
-
- {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
- {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
- (void*) PaintGlue::setFontFeatureSettings},
- {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
- (void*)PaintGlue::getFontMetrics},
- {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
- (void*)PaintGlue::getFontMetricsInt},
-
- // --------------- @CriticalNative ------------------
-
- {"nReset","(J)V", (void*) PaintGlue::reset},
- {"nSet","(JJ)V", (void*) PaintGlue::assign},
- {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
- {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
- {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
- {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
- {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
- {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
- {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
- {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
- {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
- {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
- {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
- {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
- {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
- {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
- {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
- {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong},
- {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
- {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
- {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
- {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
- {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
- {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
- {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
- {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
- {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
- {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
- {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
- {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
- {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
- {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
- {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
- {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface},
- {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
- {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
- {"nSetTextLocalesByMinikinLocaleListId","(JI)V",
- (void*) PaintGlue::setTextLocalesByMinikinLocaleListId},
- {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
- {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
- {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
- {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
- {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
- {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
- {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
- {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
- {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
- {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
- {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing},
- {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing},
- {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit},
- {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit},
- {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit},
- {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit},
- {"nAscent","(J)F", (void*) PaintGlue::ascent},
- {"nDescent","(J)F", (void*) PaintGlue::descent},
- {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition},
- {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
- {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
- {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
- {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
- {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
- {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+ {"nGetNativeFinalizer", "()J", (void*)PaintGlue::getNativeFinalizer},
+ {"nInit", "()J", (void*)PaintGlue::init},
+ {"nInitWithPaint", "(J)J", (void*)PaintGlue::initWithPaint},
+ {"nBreakText", "(J[CIIFI[F)I", (void*)PaintGlue::breakTextC},
+ {"nBreakText", "(JLjava/lang/String;ZFI[F)I", (void*)PaintGlue::breakTextS},
+ {"nGetTextAdvances", "(J[CIIIII[FI)F", (void*)PaintGlue::getTextAdvances___CIIIII_FI},
+ {"nGetTextAdvances", "(JLjava/lang/String;IIIII[FI)F",
+ (void*)PaintGlue::getTextAdvances__StringIIIII_FI},
+
+ {"nGetTextRunCursor", "(J[CIIIII)I", (void*)PaintGlue::getTextRunCursor___C},
+ {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
+ (void*)PaintGlue::getTextRunCursor__String},
+ {"nGetTextPath", "(JI[CIIFFJ)V", (void*)PaintGlue::getTextPath___C},
+ {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*)PaintGlue::getTextPath__String},
+ {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
+ (void*)PaintGlue::getStringBounds},
+ {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
+ (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",
+ (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F},
+ {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+ {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+ (void*)PaintGlue::getFontMetricsIntForText___C},
+ {"nGetFontMetricsIntForText",
+ "(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+ (void*)PaintGlue::getFontMetricsIntForText___String},
+
+ // --------------- @FastNative ----------------------
+
+ {"nSetTextLocales", "(JLjava/lang/String;)I", (void*)PaintGlue::setTextLocales},
+ {"nSetFontFeatureSettings", "(JLjava/lang/String;)V",
+ (void*)PaintGlue::setFontFeatureSettings},
+ {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
+ (void*)PaintGlue::getFontMetrics},
+ {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
+ (void*)PaintGlue::getFontMetricsInt},
+
+ // --------------- @CriticalNative ------------------
+
+ {"nReset", "(J)V", (void*)PaintGlue::reset},
+ {"nSet", "(JJ)V", (void*)PaintGlue::assign},
+ {"nGetFlags", "(J)I", (void*)PaintGlue::getFlags},
+ {"nSetFlags", "(JI)V", (void*)PaintGlue::setFlags},
+ {"nGetHinting", "(J)I", (void*)PaintGlue::getHinting},
+ {"nSetHinting", "(JI)V", (void*)PaintGlue::setHinting},
+ {"nSetAntiAlias", "(JZ)V", (void*)PaintGlue::setAntiAlias},
+ {"nSetSubpixelText", "(JZ)V", (void*)PaintGlue::setSubpixelText},
+ {"nSetLinearText", "(JZ)V", (void*)PaintGlue::setLinearText},
+ {"nSetUnderlineText", "(JZ)V", (void*)PaintGlue::setUnderlineText},
+ {"nSetStrikeThruText", "(JZ)V", (void*)PaintGlue::setStrikeThruText},
+ {"nSetFakeBoldText", "(JZ)V", (void*)PaintGlue::setFakeBoldText},
+ {"nSetFilterBitmap", "(JZ)V", (void*)PaintGlue::setFilterBitmap},
+ {"nSetDither", "(JZ)V", (void*)PaintGlue::setDither},
+ {"nGetStyle", "(J)I", (void*)PaintGlue::getStyle},
+ {"nSetStyle", "(JI)V", (void*)PaintGlue::setStyle},
+ {"nSetColor", "(JI)V", (void*)PaintGlue::setColor},
+ {"nSetColor", "(JJJ)V", (void*)PaintGlue::setColorLong},
+ {"nSetAlpha", "(JI)V", (void*)PaintGlue::setAlpha},
+ {"nGetStrokeWidth", "(J)F", (void*)PaintGlue::getStrokeWidth},
+ {"nSetStrokeWidth", "(JF)V", (void*)PaintGlue::setStrokeWidth},
+ {"nGetStrokeMiter", "(J)F", (void*)PaintGlue::getStrokeMiter},
+ {"nSetStrokeMiter", "(JF)V", (void*)PaintGlue::setStrokeMiter},
+ {"nGetStrokeCap", "(J)I", (void*)PaintGlue::getStrokeCap},
+ {"nSetStrokeCap", "(JI)V", (void*)PaintGlue::setStrokeCap},
+ {"nGetStrokeJoin", "(J)I", (void*)PaintGlue::getStrokeJoin},
+ {"nSetStrokeJoin", "(JI)V", (void*)PaintGlue::setStrokeJoin},
+ {"nGetFillPath", "(JJJ)Z", (void*)PaintGlue::getFillPath},
+ {"nSetShader", "(JJ)J", (void*)PaintGlue::setShader},
+ {"nSetColorFilter", "(JJ)J", (void*)PaintGlue::setColorFilter},
+ {"nSetXfermode", "(JI)V", (void*)PaintGlue::setXfermode},
+ {"nSetPathEffect", "(JJ)J", (void*)PaintGlue::setPathEffect},
+ {"nSetMaskFilter", "(JJ)J", (void*)PaintGlue::setMaskFilter},
+ {"nSetTypeface", "(JJ)V", (void*)PaintGlue::setTypeface},
+ {"nGetTextAlign", "(J)I", (void*)PaintGlue::getTextAlign},
+ {"nSetTextAlign", "(JI)V", (void*)PaintGlue::setTextAlign},
+ {"nSetTextLocalesByMinikinLocaleListId", "(JI)V",
+ (void*)PaintGlue::setTextLocalesByMinikinLocaleListId},
+ {"nIsElegantTextHeight", "(J)Z", (void*)PaintGlue::isElegantTextHeight},
+ {"nSetElegantTextHeight", "(JZ)V", (void*)PaintGlue::setElegantTextHeight},
+ {"nGetTextSize", "(J)F", (void*)PaintGlue::getTextSize},
+ {"nSetTextSize", "(JF)V", (void*)PaintGlue::setTextSize},
+ {"nGetTextScaleX", "(J)F", (void*)PaintGlue::getTextScaleX},
+ {"nSetTextScaleX", "(JF)V", (void*)PaintGlue::setTextScaleX},
+ {"nGetTextSkewX", "(J)F", (void*)PaintGlue::getTextSkewX},
+ {"nSetTextSkewX", "(JF)V", (void*)PaintGlue::setTextSkewX},
+ {"nGetLetterSpacing", "(J)F", (void*)PaintGlue::getLetterSpacing},
+ {"nSetLetterSpacing", "(JF)V", (void*)PaintGlue::setLetterSpacing},
+ {"nGetWordSpacing", "(J)F", (void*)PaintGlue::getWordSpacing},
+ {"nSetWordSpacing", "(JF)V", (void*)PaintGlue::setWordSpacing},
+ {"nGetStartHyphenEdit", "(J)I", (void*)PaintGlue::getStartHyphenEdit},
+ {"nGetEndHyphenEdit", "(J)I", (void*)PaintGlue::getEndHyphenEdit},
+ {"nSetStartHyphenEdit", "(JI)V", (void*)PaintGlue::setStartHyphenEdit},
+ {"nSetEndHyphenEdit", "(JI)V", (void*)PaintGlue::setEndHyphenEdit},
+ {"nAscent", "(J)F", (void*)PaintGlue::ascent},
+ {"nDescent", "(J)F", (void*)PaintGlue::descent},
+ {"nGetUnderlinePosition", "(J)F", (void*)PaintGlue::getUnderlinePosition},
+ {"nGetUnderlineThickness", "(J)F", (void*)PaintGlue::getUnderlineThickness},
+ {"nGetStrikeThruPosition", "(J)F", (void*)PaintGlue::getStrikeThruPosition},
+ {"nGetStrikeThruThickness", "(J)F", (void*)PaintGlue::getStrikeThruThickness},
+ {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
+ {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
+ {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
-
int register_android_graphics_Paint(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}
diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp
index d67bcf221681..3694ce07b972 100644
--- a/libs/hwui/jni/Path.cpp
+++ b/libs/hwui/jni/Path.cpp
@@ -102,6 +102,18 @@ public:
obj->rQuadTo(dx1, dy1, dx2, dy2);
}
+ static void conicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2,
+ jfloat y2, jfloat weight) {
+ SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+ obj->conicTo(x1, y1, x2, y2, weight);
+ }
+
+ static void rConicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1,
+ jfloat dx2, jfloat dy2, jfloat weight) {
+ SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+ obj->rConicTo(dx1, dy1, dx2, dy2, weight);
+ }
+
static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -209,6 +221,14 @@ public:
obj->setLastPt(dx, dy);
}
+ static jboolean interpolate(JNIEnv* env, jclass clazz, jlong startHandle, jlong endHandle,
+ jfloat t, jlong interpolatedHandle) {
+ SkPath* startPath = reinterpret_cast<SkPath*>(startHandle);
+ SkPath* endPath = reinterpret_cast<SkPath*>(endHandle);
+ SkPath* interpolatedPath = reinterpret_cast<SkPath*>(interpolatedHandle);
+ return startPath->interpolate(*endPath, t, interpolatedPath);
+ }
+
static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle,
jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -473,6 +493,16 @@ public:
// ---------------- @CriticalNative -------------------------
+ static jint getGenerationID(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle) {
+ return (reinterpret_cast<SkPath*>(pathHandle)->getGenerationID());
+ }
+
+ static jboolean isInterpolatable(CRITICAL_JNI_PARAMS_COMMA jlong startHandle, jlong endHandle) {
+ SkPath* startPath = reinterpret_cast<SkPath*>(startHandle);
+ SkPath* endPath = reinterpret_cast<SkPath*>(endHandle);
+ return startPath->isInterpolatable(*endPath);
+ }
+
static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->reset();
@@ -506,48 +536,53 @@ public:
};
static const JNINativeMethod methods[] = {
- {"nInit","()J", (void*) SkPathGlue::init},
- {"nInit","(J)J", (void*) SkPathGlue::init_Path},
- {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
- {"nSet","(JJ)V", (void*) SkPathGlue::set},
- {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
- {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
- {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
- {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
- {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
- {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
- {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
- {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
- {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
- {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
- {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
- {"nClose","(J)V", (void*) SkPathGlue::close},
- {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
- {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
- {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
- {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
- {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
- {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
- {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
- {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
- {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
- {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF},
- {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
- {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
- {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
- {"nOp","(JJIJ)Z", (void*) SkPathGlue::op},
- {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate},
-
- // ------- @FastNative below here ----------------------
- {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
-
- // ------- @CriticalNative below here ------------------
- {"nReset","(J)V", (void*) SkPathGlue::reset},
- {"nRewind","(J)V", (void*) SkPathGlue::rewind},
- {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
- {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex},
- {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType},
- {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType},
+ {"nInit", "()J", (void*)SkPathGlue::init},
+ {"nInit", "(J)J", (void*)SkPathGlue::init_Path},
+ {"nGetFinalizer", "()J", (void*)SkPathGlue::getFinalizer},
+ {"nSet", "(JJ)V", (void*)SkPathGlue::set},
+ {"nComputeBounds", "(JLandroid/graphics/RectF;)V", (void*)SkPathGlue::computeBounds},
+ {"nIncReserve", "(JI)V", (void*)SkPathGlue::incReserve},
+ {"nMoveTo", "(JFF)V", (void*)SkPathGlue::moveTo__FF},
+ {"nRMoveTo", "(JFF)V", (void*)SkPathGlue::rMoveTo},
+ {"nLineTo", "(JFF)V", (void*)SkPathGlue::lineTo__FF},
+ {"nRLineTo", "(JFF)V", (void*)SkPathGlue::rLineTo},
+ {"nQuadTo", "(JFFFF)V", (void*)SkPathGlue::quadTo__FFFF},
+ {"nRQuadTo", "(JFFFF)V", (void*)SkPathGlue::rQuadTo},
+ {"nConicTo", "(JFFFFF)V", (void*)SkPathGlue::conicTo},
+ {"nRConicTo", "(JFFFFF)V", (void*)SkPathGlue::rConicTo},
+ {"nCubicTo", "(JFFFFFF)V", (void*)SkPathGlue::cubicTo__FFFFFF},
+ {"nRCubicTo", "(JFFFFFF)V", (void*)SkPathGlue::rCubicTo},
+ {"nArcTo", "(JFFFFFFZ)V", (void*)SkPathGlue::arcTo},
+ {"nClose", "(J)V", (void*)SkPathGlue::close},
+ {"nAddRect", "(JFFFFI)V", (void*)SkPathGlue::addRect},
+ {"nAddOval", "(JFFFFI)V", (void*)SkPathGlue::addOval},
+ {"nAddCircle", "(JFFFI)V", (void*)SkPathGlue::addCircle},
+ {"nAddArc", "(JFFFFFF)V", (void*)SkPathGlue::addArc},
+ {"nAddRoundRect", "(JFFFFFFI)V", (void*)SkPathGlue::addRoundRectXY},
+ {"nAddRoundRect", "(JFFFF[FI)V", (void*)SkPathGlue::addRoundRect8},
+ {"nAddPath", "(JJFF)V", (void*)SkPathGlue::addPath__PathFF},
+ {"nAddPath", "(JJ)V", (void*)SkPathGlue::addPath__Path},
+ {"nAddPath", "(JJJ)V", (void*)SkPathGlue::addPath__PathMatrix},
+ {"nInterpolate", "(JJFJ)Z", (void*)SkPathGlue::interpolate},
+ {"nOffset", "(JFF)V", (void*)SkPathGlue::offset__FF},
+ {"nSetLastPoint", "(JFF)V", (void*)SkPathGlue::setLastPoint},
+ {"nTransform", "(JJJ)V", (void*)SkPathGlue::transform__MatrixPath},
+ {"nTransform", "(JJ)V", (void*)SkPathGlue::transform__Matrix},
+ {"nOp", "(JJIJ)Z", (void*)SkPathGlue::op},
+ {"nApproximate", "(JF)[F", (void*)SkPathGlue::approximate},
+
+ // ------- @FastNative below here ----------------------
+ {"nIsRect", "(JLandroid/graphics/RectF;)Z", (void*)SkPathGlue::isRect},
+
+ // ------- @CriticalNative below here ------------------
+ {"nGetGenerationID", "(J)I", (void*)SkPathGlue::getGenerationID},
+ {"nIsInterpolatable", "(JJ)Z", (void*)SkPathGlue::isInterpolatable},
+ {"nReset", "(J)V", (void*)SkPathGlue::reset},
+ {"nRewind", "(J)V", (void*)SkPathGlue::rewind},
+ {"nIsEmpty", "(J)Z", (void*)SkPathGlue::isEmpty},
+ {"nIsConvex", "(J)Z", (void*)SkPathGlue::isConvex},
+ {"nGetFillType", "(J)I", (void*)SkPathGlue::getFillType},
+ {"nSetFillType", "(JI)V", (void*)SkPathGlue::setFillType},
};
int register_android_graphics_Path(JNIEnv* env) {
diff --git a/libs/hwui/jni/PathIterator.cpp b/libs/hwui/jni/PathIterator.cpp
new file mode 100644
index 000000000000..3884342d8d37
--- /dev/null
+++ b/libs/hwui/jni/PathIterator.cpp
@@ -0,0 +1,81 @@
+/* libs/android_runtime/android/graphics/PathMeasure.cpp
+**
+** Copyright 2007, 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 <log/log.h>
+
+#include "GraphicsJNI.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+
+namespace android {
+
+class SkPathIteratorGlue {
+public:
+ static void finalizer(SkPath::RawIter* obj) { delete obj; }
+
+ static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+ }
+
+ static jlong create(JNIEnv* env, jobject clazz, jlong pathHandle) {
+ const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ return reinterpret_cast<jlong>(new SkPath::RawIter(*path));
+ }
+
+ // ---------------- @CriticalNative -------------------------
+
+ static jint peek(CRITICAL_JNI_PARAMS_COMMA jlong iteratorHandle) {
+ SkPath::RawIter* iterator = reinterpret_cast<SkPath::RawIter*>(iteratorHandle);
+ return iterator->peek();
+ }
+
+ static jint next(CRITICAL_JNI_PARAMS_COMMA jlong iteratorHandle, jlong pointsArray) {
+ static_assert(SkPath::kMove_Verb == 0, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kLine_Verb == 1, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kQuad_Verb == 2, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kConic_Verb == 3, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kCubic_Verb == 4, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kClose_Verb == 5, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kDone_Verb == 6, "SkPath::Verb unexpected index");
+
+ SkPath::RawIter* iterator = reinterpret_cast<SkPath::RawIter*>(iteratorHandle);
+ float* points = reinterpret_cast<float*>(pointsArray);
+ SkPath::Verb verb =
+ static_cast<SkPath::Verb>(iterator->next(reinterpret_cast<SkPoint*>(points)));
+ if (verb == SkPath::kConic_Verb) {
+ float weight = iterator->conicWeight();
+ points[6] = weight;
+ }
+ return static_cast<int>(verb);
+ }
+};
+
+static const JNINativeMethod methods[] = {
+ {"nCreate", "(J)J", (void*)SkPathIteratorGlue::create},
+ {"nGetFinalizer", "()J", (void*)SkPathIteratorGlue::getFinalizer},
+
+ // ------- @CriticalNative below here ------------------
+
+ {"nPeek", "(J)I", (void*)SkPathIteratorGlue::peek},
+ {"nNext", "(JJ)I", (void*)SkPathIteratorGlue::next},
+};
+
+int register_android_graphics_PathIterator(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/PathIterator", methods, NELEM(methods));
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 213f35a81b88..f3db1705e694 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -15,6 +15,7 @@
*/
#include "Bitmap.h"
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkImageFilter.h"
#include "SkImageFilters.h"
#include "graphics_jni_helpers.h"
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 0bbd8a8cf97c..fa8e2e79c831 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -2,11 +2,21 @@
#define LOG_TAG "ShaderJNI"
#include "GraphicsJNI.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
+#include "SkColor.h"
#include "SkColorFilter.h"
#include "SkGradientShader.h"
+#include "SkImage.h"
#include "SkImagePriv.h"
+#include "SkMatrix.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+#include "SkSamplingOptions.h"
+#include "SkScalar.h"
#include "SkShader.h"
-#include "SkBlendMode.h"
+#include "SkString.h"
+#include "SkTileMode.h"
#include "include/effects/SkRuntimeEffect.h"
#include <vector>
@@ -16,7 +26,7 @@ using namespace android::uirenderer;
/**
* By default Skia gradients will interpolate their colors in unpremul space
* and then premultiply each of the results. We must set this flag to preserve
- * backwards compatiblity by premultiplying the colors of the gradient first,
+ * backwards compatibility by premultiplying the colors of the gradient first,
* and then interpolating between them.
*/
static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index d86d9ee56f4c..209b35c5537c 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -20,18 +20,21 @@
#include <minikin/FontCollection.h>
#include <minikin/FontFamily.h>
#include <minikin/FontFileParser.h>
+#include <minikin/LocaleList.h>
+#include <minikin/MinikinFontFactory.h>
#include <minikin/SystemFonts.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
+
+#include <mutex>
+#include <unordered_map>
+
#include "FontUtils.h"
#include "GraphicsJNI.h"
#include "SkData.h"
#include "SkTypeface.h"
#include "fonts/Font.h"
-#include <mutex>
-#include <unordered_map>
-
#ifdef __ANDROID__
#include <sys/stat.h>
#endif
@@ -106,27 +109,14 @@ static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
jlong fallbackPtr, int weight, int italic) {
ScopedLongArrayRO families(env, familyArray);
- std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
Typeface* typeface = (fallbackPtr == 0) ? nullptr : toTypeface(fallbackPtr);
- if (typeface != nullptr) {
- const std::vector<std::shared_ptr<minikin::FontFamily>>& fallbackFamilies =
- toTypeface(fallbackPtr)->fFontCollection->getFamilies();
- familyVec.reserve(families.size() + fallbackFamilies.size());
- for (size_t i = 0; i < families.size(); i++) {
- FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
- familyVec.emplace_back(family->family);
- }
- for (size_t i = 0; i < fallbackFamilies.size(); i++) {
- familyVec.emplace_back(fallbackFamilies[i]);
- }
- } else {
- familyVec.reserve(families.size());
- for (size_t i = 0; i < families.size(); i++) {
- FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
- familyVec.emplace_back(family->family);
- }
+ std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
+ familyVec.reserve(families.size());
+ for (size_t i = 0; i < families.size(); i++) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
+ familyVec.emplace_back(family->family);
}
- return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic));
+ return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic, typeface));
}
// CriticalNative
@@ -137,15 +127,13 @@ static void Typeface_setDefault(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
Typeface* face = toTypeface(faceHandle);
- const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
- const size_t length = tagSet.size();
+ const size_t length = face->fFontCollection->getSupportedAxesCount();
if (length == 0) {
return nullptr;
}
std::vector<jint> tagVec(length);
- int index = 0;
- for (const auto& tag : tagSet) {
- tagVec[index++] = tag;
+ for (size_t i = 0; i < length; i++) {
+ tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
}
std::sort(tagVec.begin(), tagVec.end());
const jintArray result = env->NewIntArray(length);
@@ -204,9 +192,18 @@ static sk_sp<SkData> makeSkDataCached(const std::string& path, bool hasVerity) {
return entry;
}
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader);
+class MinikinFontSkiaFactory : minikin::MinikinFontFactory {
+private:
+ MinikinFontSkiaFactory() : MinikinFontFactory() { MinikinFontFactory::setInstance(this); }
+
+public:
+ static void init() { static MinikinFontSkiaFactory factory; }
+ void skip(minikin::BufferReader* reader) const override;
+ std::shared_ptr<minikin::MinikinFont> create(minikin::BufferReader reader) const override;
+ void write(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) const override;
+};
-static minikin::Font::TypefaceLoader* readMinikinFontSkia(minikin::BufferReader* reader) {
+void MinikinFontSkiaFactory::skip(minikin::BufferReader* reader) const {
// Advance reader's position.
reader->skipString(); // fontPath
reader->skip<int>(); // fontIndex
@@ -216,10 +213,10 @@ static minikin::Font::TypefaceLoader* readMinikinFontSkia(minikin::BufferReader*
reader->skip<uint32_t>(); // expectedFontRevision
reader->skipString(); // expectedPostScriptName
}
- return &loadMinikinFontSkia;
}
-static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::BufferReader reader) {
+std::shared_ptr<minikin::MinikinFont> MinikinFontSkiaFactory::create(
+ minikin::BufferReader reader) const {
std::string_view fontPath = reader.readString();
std::string path(fontPath.data(), fontPath.size());
ATRACE_FORMAT("Loading font %s", path.c_str());
@@ -268,8 +265,8 @@ static std::shared_ptr<minikin::MinikinFont> loadMinikinFontSkia(minikin::Buffer
return minikinFont;
}
-static void writeMinikinFontSkia(minikin::BufferWriter* writer,
- const minikin::MinikinFont* typeface) {
+void MinikinFontSkiaFactory::write(minikin::BufferWriter* writer,
+ const minikin::MinikinFont* typeface) const {
// When you change the format of font metadata, please update code to parse
// typefaceMetadataReader() in
// frameworks/base/libs/hwui/jni/fonts/Font.cpp too.
@@ -293,7 +290,9 @@ static void writeMinikinFontSkia(minikin::BufferWriter* writer,
}
}
-static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+static jint Typeface_writeTypefaces(JNIEnv* env, jobject, jobject buffer, jint position,
+ jlongArray faceHandles) {
+ MinikinFontSkiaFactory::init();
ScopedLongArrayRO faces(env, faceHandles);
std::vector<Typeface*> typefaces;
typefaces.reserve(faces.size());
@@ -301,7 +300,12 @@ static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongA
typefaces.push_back(toTypeface(faces[i]));
}
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- minikin::BufferWriter writer(addr);
+ if (addr != nullptr &&
+ reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return 0;
+ }
+ minikin::BufferWriter writer(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
for (Typeface* typeface : typefaces) {
@@ -310,7 +314,7 @@ static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongA
fontCollections.push_back(typeface->fFontCollection);
}
}
- minikin::FontCollection::writeVector<writeMinikinFontSkia>(&writer, fontCollections);
+ minikin::FontCollection::writeVector(&writer, fontCollections);
writer.write<uint32_t>(typefaces.size());
for (Typeface* typeface : typefaces) {
writer.write<uint32_t>(fcToIndex.find(typeface->fFontCollection)->second);
@@ -321,12 +325,20 @@ static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongA
return static_cast<jint>(writer.size());
}
-static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+static jlongArray Typeface_readTypefaces(JNIEnv* env, jobject, jobject buffer, jint position) {
+ MinikinFontSkiaFactory::init();
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- if (addr == nullptr) return nullptr;
- minikin::BufferReader reader(addr);
+ if (addr == nullptr) {
+ ALOGE("Passed a null buffer.");
+ return nullptr;
+ }
+ if (reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return nullptr;
+ }
+ minikin::BufferReader reader(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
- minikin::FontCollection::readVector<readMinikinFontSkia>(&reader);
+ minikin::FontCollection::readVector(&reader);
uint32_t typefaceCount = reader.read<uint32_t>();
std::vector<jlong> faceHandles;
faceHandles.reserve(typefaceCount);
@@ -343,7 +355,6 @@ static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
return result;
}
-
static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
jobject typeface) {
ScopedUtfChars fieldNameChars(env, fieldName);
@@ -356,18 +367,6 @@ static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring f
env->SetStaticObjectField(cls, fid, typeface);
}
-// Critical Native
-static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
- return toTypeface(faceHandle)->fFontCollection->getFamilies().size();
-}
-
-// Critical Native
-static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
- std::shared_ptr<minikin::FontFamily> family =
- toTypeface(faceHandle)->fFontCollection->getFamilies()[index];
- return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
-}
-
// Regular JNI
static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
ScopedUtfChars filePath(env, jFilePath);
@@ -380,6 +379,12 @@ static void Typeface_addFontCollection(CRITICAL_JNI_PARAMS_COMMA jlong faceHandl
minikin::SystemFonts::addFontMap(std::move(collection));
}
+// Fast Native
+static void Typeface_registerLocaleList(JNIEnv* env, jobject, jstring jLocales) {
+ ScopedUtfChars locales(env, jLocales);
+ minikin::registerLocaleList(locales.c_str());
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
@@ -397,14 +402,13 @@ static const JNINativeMethod gTypefaceMethods[] = {
{"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
{"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
(void*)Typeface_registerGenericFamily},
- {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
- {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;I[J)I", (void*)Typeface_writeTypefaces},
+ {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
{"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
(void*)Typeface_forceSetStaticFinalField},
- {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
- {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
{"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
{"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
+ {"nativeRegisterLocaleList", "(Ljava/lang/String;)V", (void*)Typeface_registerLocaleList},
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h
index 6cdf44d85a5a..f6e3a0eeaa0e 100644
--- a/libs/hwui/jni/Utils.h
+++ b/libs/hwui/jni/Utils.h
@@ -17,8 +17,11 @@
#ifndef _ANDROID_GRAPHICS_UTILS_H_
#define _ANDROID_GRAPHICS_UTILS_H_
+#include "SkRefCnt.h"
#include "SkStream.h"
+class SkData;
+
#include <jni.h>
#include <androidfw/Asset.h>
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index d64d38a815f6..1c5f126d8672 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -1,5 +1,7 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "SkJPEGWriteUtility.h"
+#include "SkStream.h"
+#include "SkTypes.h"
#include "YuvToJpegEncoder.h"
#include <ui/PixelFormat.h>
#include <hardware/hardware.h>
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index 7e7b935df276..a69726b17e9d 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -1,13 +1,13 @@
#ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
-#include "SkTypes.h"
-#include "SkStream.h"
extern "C" {
#include "jpeglib.h"
#include "jerror.h"
}
+class SkWStream;
+
class YuvToJpegEncoder {
public:
/** Create an encoder based on the YUV format.
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 132234b38003..8a4d4e17edb1 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -21,6 +21,7 @@
#else
#define __ANDROID_API_P__ 28
#endif
+#include <Mesh.h>
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
@@ -30,12 +31,24 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedStringChars.h>
-#include "FontUtils.h"
#include "Bitmap.h"
+#include "FontUtils.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
+#include "SkClipOp.h"
+#include "SkColor.h"
+#include "SkColorSpace.h"
#include "SkGraphics.h"
+#include "SkImageInfo.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRRect.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
#include "SkRegion.h"
+#include "SkScalar.h"
#include "SkVertices.h"
-#include "SkRRect.h"
namespace minikin {
class MeasuredText;
@@ -431,6 +444,14 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
blendMode, *paint);
}
+static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle,
+ jlong paintHandle) {
+ const SkMesh mesh = reinterpret_cast<MeshWrapper*>(meshHandle)->mesh;
+ SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle);
+ SkPaint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->drawMesh(mesh, SkBlender::Mode(blendMode), *paint);
+}
+
static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom,
jlong paintHandle, jint dstDensity, jint srcDensity) {
@@ -701,9 +722,10 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
}
static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
- jfloat bottom, jfloat rx, jfloat ry) {
+ jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) {
auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
- canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
+ canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry),
+ alpha);
}
}; // namespace CanvasJNI
@@ -748,38 +770,38 @@ static const JNINativeMethod gMethods[] = {
// If called from Canvas these are regular JNI
// If called from DisplayListCanvas they are @FastNative
static const JNINativeMethod gDrawMethods[] = {
- {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor},
- {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong},
- {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
- {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
- {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
- {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
- {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
- {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
- {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
- {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
- {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
- {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
- {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
- {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
- {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
- {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
- {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
- {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
- {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
- {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
- {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
- {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
- {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
- {"nDrawGlyphs", "(J[I[FIIIJJ)V", (void*)CanvasJNI::drawGlyphs},
- {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
- {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
- {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
- {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
- {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
- {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
- {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
-};
+ {"nDrawColor", "(JII)V", (void*)CanvasJNI::drawColor},
+ {"nDrawColor", "(JJJI)V", (void*)CanvasJNI::drawColorLong},
+ {"nDrawPaint", "(JJ)V", (void*)CanvasJNI::drawPaint},
+ {"nDrawPoint", "(JFFJ)V", (void*)CanvasJNI::drawPoint},
+ {"nDrawPoints", "(J[FIIJ)V", (void*)CanvasJNI::drawPoints},
+ {"nDrawLine", "(JFFFFJ)V", (void*)CanvasJNI::drawLine},
+ {"nDrawLines", "(J[FIIJ)V", (void*)CanvasJNI::drawLines},
+ {"nDrawRect", "(JFFFFJ)V", (void*)CanvasJNI::drawRect},
+ {"nDrawRegion", "(JJJ)V", (void*)CanvasJNI::drawRegion},
+ {"nDrawRoundRect", "(JFFFFFFJ)V", (void*)CanvasJNI::drawRoundRect},
+ {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*)CanvasJNI::drawDoubleRoundRectXY},
+ {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*)CanvasJNI::drawDoubleRoundRectRadii},
+ {"nDrawCircle", "(JFFFJ)V", (void*)CanvasJNI::drawCircle},
+ {"nDrawOval", "(JFFFFJ)V", (void*)CanvasJNI::drawOval},
+ {"nDrawArc", "(JFFFFFFZJ)V", (void*)CanvasJNI::drawArc},
+ {"nDrawPath", "(JJJ)V", (void*)CanvasJNI::drawPath},
+ {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+ {"nDrawMesh", "(JJIJ)V", (void*)CanvasJNI::drawMesh},
+ {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
+ {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+ {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+ {"nDrawBitmap", "(JJFFJIII)V", (void*)CanvasJNI::drawBitmap},
+ {"nDrawBitmap", "(JJFFFFFFFFJII)V", (void*)CanvasJNI::drawBitmapRect},
+ {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+ {"nDrawGlyphs", "(J[I[FIIIJJ)V", (void*)CanvasJNI::drawGlyphs},
+ {"nDrawText", "(J[CIIFFIJ)V", (void*)CanvasJNI::drawTextChars},
+ {"nDrawText", "(JLjava/lang/String;IIFFIJ)V", (void*)CanvasJNI::drawTextString},
+ {"nDrawTextRun", "(J[CIIIIFFZJJ)V", (void*)CanvasJNI::drawTextRunChars},
+ {"nDrawTextRun", "(JLjava/lang/String;IIIIFFZJ)V", (void*)CanvasJNI::drawTextRunString},
+ {"nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*)CanvasJNI::drawTextOnPathChars},
+ {"nDrawTextOnPath", "(JLjava/lang/String;JFFIJ)V", (void*)CanvasJNI::drawTextOnPathString},
+ {"nPunchHole", "(JFFFFFFF)V", (void*)CanvasJNI::punchHole}};
int register_android_graphics_Canvas(JNIEnv* env) {
int ret = 0;
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
new file mode 100644
index 000000000000..4886fdd7ac67
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HardwareBufferRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <GraphicsJNI.h>
+#include <RootRenderNode.h>
+#include <TreeInfo.h>
+#include <android-base/unique_fd.h>
+#include <android/native_window.h>
+#include <nativehelper/JNIPlatformHelp.h>
+#include <renderthread/CanvasContext.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderThread.h>
+
+#include "HardwareBufferHelpers.h"
+#include "JvmErrorReporter.h"
+
+namespace android {
+
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+struct {
+ jclass clazz;
+ jmethodID invokeRenderCallback;
+} gHardwareBufferRendererClassInfo;
+
+static RenderCallback createRenderCallback(JNIEnv* env, jobject releaseCallback) {
+ if (releaseCallback == nullptr) return nullptr;
+
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef =
+ std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(releaseCallback));
+ return [globalCallbackRef](android::base::unique_fd&& fd, int status) {
+ GraphicsJNI::getJNIEnv()->CallStaticVoidMethod(
+ gHardwareBufferRendererClassInfo.clazz,
+ gHardwareBufferRendererClassInfo.invokeRenderCallback, globalCallbackRef->object(),
+ reinterpret_cast<jint>(fd.release()), reinterpret_cast<jint>(status));
+ };
+}
+
+static long android_graphics_HardwareBufferRenderer_createRootNode(JNIEnv* env, jobject) {
+ auto* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env));
+ node->incStrong(nullptr);
+ node->setName("RootRenderNode");
+ return reinterpret_cast<jlong>(node);
+}
+
+static void android_graphics_hardwareBufferRenderer_destroyRootNode(JNIEnv*, jobject,
+ jlong renderNodePtr) {
+ auto* node = reinterpret_cast<RootRenderNode*>(renderNodePtr);
+ node->destroy();
+}
+
+static long android_graphics_HardwareBufferRenderer_create(JNIEnv* env, jobject, jobject buffer,
+ jlong renderNodePtr) {
+ auto* hardwareBuffer = HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(env, buffer);
+ auto* rootRenderNode = reinterpret_cast<RootRenderNode*>(renderNodePtr);
+ ContextFactoryImpl factory(rootRenderNode);
+ auto* proxy = new RenderProxy(true, rootRenderNode, &factory);
+ proxy->setHardwareBuffer(hardwareBuffer);
+ return (jlong)proxy;
+}
+
+static void HardwareBufferRenderer_destroy(jobject renderProxy) {
+ auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy);
+ delete proxy;
+}
+
+static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) {
+ auto matrix = SkMatrix();
+ switch (transform) {
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
+ matrix.setRotate(90);
+ matrix.postTranslate(width, 0);
+ break;
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
+ matrix.setRotate(180);
+ matrix.postTranslate(width, height);
+ break;
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
+ matrix.setRotate(270);
+ matrix.postTranslate(0, width);
+ break;
+ default:
+ ALOGE("Invalid transform provided. Transform should be validated from"
+ "the java side. Leveraging identity transform as a fallback");
+ [[fallthrough]];
+ case ANATIVEWINDOW_TRANSFORM_IDENTITY:
+ break;
+ }
+ return matrix;
+}
+
+static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jobject renderProxy,
+ jint transform, jint width, jint height,
+ jlong colorspacePtr, jobject consumer) {
+ auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy);
+ auto skWidth = static_cast<SkScalar>(width);
+ auto skHeight = static_cast<SkScalar>(height);
+ auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform);
+ auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr);
+ proxy->setHardwareBufferRenderParams(
+ HardwareBufferRenderParams(matrix, colorSpace, createRenderCallback(env, consumer)));
+ return proxy->syncAndDrawFrame();
+}
+
+static void android_graphics_HardwareBufferRenderer_setLightGeometry(JNIEnv*, jobject,
+ jobject renderProxyPtr,
+ jfloat lightX, jfloat lightY,
+ jfloat lightZ,
+ jfloat lightRadius) {
+ auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr);
+ proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius);
+}
+
+static void android_graphics_HardwareBufferRenderer_setLightAlpha(JNIEnv* env, jobject,
+ jobject renderProxyPtr,
+ jfloat ambientShadowAlpha,
+ jfloat spotShadowAlpha) {
+ auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr);
+ proxy->setLightAlpha((uint8_t)(255 * ambientShadowAlpha), (uint8_t)(255 * spotShadowAlpha));
+}
+
+static jlong android_graphics_HardwareBufferRenderer_getFinalizer() {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&HardwareBufferRenderer_destroy));
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/HardwareBufferRenderer";
+
+static const JNINativeMethod gMethods[] = {
+ {"nCreateHardwareBufferRenderer", "(Landroid/hardware/HardwareBuffer;J)J",
+ (void*)android_graphics_HardwareBufferRenderer_create},
+ {"nRender", "(JIIIJLjava/util/function/Consumer;)I",
+ (void*)android_graphics_HardwareBufferRenderer_render},
+ {"nCreateRootRenderNode", "()J",
+ (void*)android_graphics_HardwareBufferRenderer_createRootNode},
+ {"nSetLightGeometry", "(JFFFF)V",
+ (void*)android_graphics_HardwareBufferRenderer_setLightGeometry},
+ {"nSetLightAlpha", "(JFF)V", (void*)android_graphics_HardwareBufferRenderer_setLightAlpha},
+ {"nGetFinalizer", "()J", (void*)android_graphics_HardwareBufferRenderer_getFinalizer},
+ {"nDestroyRootRenderNode", "(J)V",
+ (void*)android_graphics_hardwareBufferRenderer_destroyRootNode}};
+
+int register_android_graphics_HardwareBufferRenderer(JNIEnv* env) {
+ jclass hardwareBufferRendererClazz =
+ FindClassOrDie(env, "android/graphics/HardwareBufferRenderer");
+ gHardwareBufferRendererClassInfo.clazz = hardwareBufferRendererClazz;
+ gHardwareBufferRendererClassInfo.invokeRenderCallback =
+ GetStaticMethodIDOrDie(env, hardwareBufferRendererClazz, "invokeRenderCallback",
+ "(Ljava/util/function/Consumer;II)V");
+ HardwareBufferHelpers::init();
+ return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+} // namespace android \ No newline at end of file
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c48448dffdd2..47e2edb2ed0f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -23,8 +23,16 @@
#include <Picture.h>
#include <Properties.h>
#include <RootRenderNode.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkData.h>
+#include <SkImage.h>
#include <SkImagePriv.h>
+#include <SkPicture.h>
+#include <SkPixmap.h>
#include <SkSerialProcs.h>
+#include <SkStream.h>
+#include <SkTypeface.h>
#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <inttypes.h>
@@ -48,6 +56,7 @@
#include <atomic>
#include <vector>
+#include "JvmErrorReporter.h"
#include "android_graphics_HardwareRendererObserver.h"
namespace android {
@@ -80,35 +89,17 @@ struct {
jmethodID onFrameComplete;
} gFrameCompleteCallback;
-static JNIEnv* getenv(JavaVM* vm) {
- JNIEnv* env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
- }
- return env;
-}
+struct {
+ jmethodID onCopyFinished;
+ jmethodID getDestinationBitmap;
+} gCopyRequest;
typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
ANW_fromSurface fromSurface;
-class JvmErrorReporter : public ErrorHandler {
-public:
- JvmErrorReporter(JNIEnv* env) {
- env->GetJavaVM(&mVm);
- }
-
- virtual void onError(const std::string& message) override {
- JNIEnv* env = getenv(mVm);
- jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
- }
-private:
- JavaVM* mVm;
-};
-
class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
public:
explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
- env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
@@ -118,19 +109,18 @@ public:
void onFrameCommit(bool didProduceBuffer) {
if (mObject) {
ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
- getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
- didProduceBuffer);
+ GraphicsJNI::getJNIEnv()->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
+ didProduceBuffer);
releaseObject();
}
}
private:
- JavaVM* mVm;
jobject mObject;
void releaseObject() {
if (mObject) {
- getenv(mVm)->DeleteGlobalRef(mObject);
+ GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject);
mObject = nullptr;
}
}
@@ -258,6 +248,16 @@ static void android_view_ThreadedRenderer_setIsHighEndGfx(JNIEnv* env, jobject c
Properties::setIsHighEndGfx(jIsHighEndGfx);
}
+static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz,
+ jboolean isLowRam) {
+ Properties::setIsLowRam(isLowRam);
+}
+
+static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz,
+ jboolean isSystemOrPersistent) {
+ Properties::setIsSystemOrPersistent(isSystemOrPersistent);
+}
+
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo,
jint frameInfoSize) {
@@ -420,26 +420,6 @@ static void android_view_ThreadedRenderer_forceDrawNextFrame(JNIEnv* env, jobjec
proxy->forceDrawNextFrame();
}
-class JGlobalRefHolder {
-public:
- JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
-
- virtual ~JGlobalRefHolder() {
- getenv(mVm)->DeleteGlobalRef(mObject);
- mObject = nullptr;
- }
-
- jobject object() { return mObject; }
- JavaVM* vm() { return mVm; }
-
-private:
- JGlobalRefHolder(const JGlobalRefHolder&) = delete;
- void operator=(const JGlobalRefHolder&) = delete;
-
- JavaVM* mVm;
- jobject mObject;
-};
-
using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;
struct PictureCaptureState {
@@ -451,7 +431,7 @@ struct PictureCaptureState {
};
// TODO: This & Multi-SKP & Single-SKP should all be de-duped into
-// a single "make a SkPicture serailizable-safe" utility somewhere
+// a single "make a SkPicture serializable-safe" utility somewhere
class PictureWrapper : public Picture {
public:
PictureWrapper(sk_sp<SkPicture>&& src, const std::shared_ptr<PictureCaptureState>& state)
@@ -555,7 +535,7 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv*
auto pictureState = std::make_shared<PictureCaptureState>();
proxy->setPictureCapturedCallback([globalCallbackRef,
pictureState](sk_sp<SkPicture>&& picture) {
- JNIEnv* env = getenv(globalCallbackRef->vm());
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
Picture* wrapper = new PictureWrapper{std::move(picture), pictureState};
env->CallStaticVoidMethod(gHardwareRenderer.clazz,
gHardwareRenderer.invokePictureCapturedCallback,
@@ -577,7 +557,7 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
vm, env->NewGlobalRef(aSurfaceTransactionCallback));
proxy->setASurfaceTransactionCallback(
[globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool {
- JNIEnv* env = getenv(globalCallbackRef->vm());
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
jboolean ret = env->CallBooleanMethod(
globalCallbackRef->object(),
gASurfaceTransactionCallback.onMergeTransaction,
@@ -599,7 +579,7 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall
auto globalCallbackRef =
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
- JNIEnv* env = getenv(globalCallbackRef->vm());
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
env->CallVoidMethod(globalCallbackRef->object(),
gPrepareSurfaceControlForWebviewCallback.prepare);
});
@@ -618,7 +598,7 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
env->NewGlobalRef(frameCallback));
proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
int64_t frameNr) -> std::function<void(bool)> {
- JNIEnv* env = getenv(globalCallbackRef->vm());
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
ScopedLocalRef<jobject> frameCommitCallback(
env, env->CallObjectMethod(
globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
@@ -657,22 +637,48 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
auto globalCallbackRef =
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setFrameCompleteCallback([globalCallbackRef]() {
- JNIEnv* env = getenv(globalCallbackRef->vm());
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
env->CallVoidMethod(globalCallbackRef->object(),
gFrameCompleteCallback.onFrameComplete);
});
}
}
-static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
- jobject clazz, jobject jsurface, jint left, jint top,
- jint right, jint bottom, jlong bitmapPtr) {
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+class CopyRequestAdapter : public CopyRequest {
+public:
+ CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+ : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
+ jlong bitmapPtr = env->CallLongMethod(
+ mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
+ SkBitmap bitmap;
+ bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ JNIEnv* env = GraphicsJNI::getJNIEnv();
+ env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+ static_cast<jint>(result));
+ }
+
+private:
+ JGlobalRefHolder mRefHolder;
+};
+
+static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz,
+ jobject jsurface, jint left, jint top,
+ jint right, jint bottom,
+ jobject jCopyRequest) {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
+ Rect(left, top, right, bottom));
ANativeWindow* window = fromSurface(env, jsurface);
- jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+ RenderProxy::copySurfaceInto(window, std::move(copyRequest));
ANativeWindow_release(window);
- return result;
}
class ContextFactory : public IContextFactory {
@@ -811,6 +817,11 @@ static void android_view_ThreadedRenderer_setRtAnimationsEnabled(JNIEnv* env, jo
RenderProxy::setRtAnimationsEnabled(enabled);
}
+static void android_view_ThreadedRenderer_notifyCallbackPending(JNIEnv*, jclass, jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->notifyCallbackPending();
+}
+
// Plumbs the display density down to DeviceInfo.
static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, jint densityDpi) {
// Convert from dpi to density-independent pixels.
@@ -818,17 +829,19 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass,
DeviceInfo::setDensity(density);
}
-static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
+static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv* env, jclass, jint physicalWidth,
jint physicalHeight, jfloat refreshRate,
jint wideColorDataspace,
jlong appVsyncOffsetNanos,
- jlong presentationDeadlineNanos) {
+ jlong presentationDeadlineNanos,
+ jboolean supportFp16ForHdr) {
DeviceInfo::setWidth(physicalWidth);
DeviceInfo::setHeight(physicalHeight);
DeviceInfo::setRefreshRate(refreshRate);
DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
+ DeviceInfo::setSupportFp16ForHdr(supportFp16ForHdr);
}
static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) {
@@ -910,6 +923,9 @@ static const JNINativeMethod gMethods[] = {
{"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
{"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
{"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
+ {"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam},
+ {"nSetIsSystemOrPersistent", "(Z)V",
+ (void*)android_view_ThreadedRenderer_setIsSystemOrPersistent},
{"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
{"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
{"nRegisterAnimatingRenderNode", "(JJ)V",
@@ -961,7 +977,8 @@ static const JNINativeMethod gMethods[] = {
(void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
{"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver},
{"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver},
- {"nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I",
+ {"nCopySurfaceInto",
+ "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V",
(void*)android_view_ThreadedRenderer_copySurfaceInto},
{"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
(void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode},
@@ -974,7 +991,7 @@ static const JNINativeMethod gMethods[] = {
{"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
{"nSetDisplayDensityDpi", "(I)V",
(void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
- {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
+ {"nInitDisplayInfo", "(IIFIJJZ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
{"isWebViewOverlaysEnabled", "()Z",
(void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled},
@@ -982,6 +999,8 @@ static const JNINativeMethod gMethods[] = {
{"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled},
{"nSetRtAnimationsEnabled", "(Z)V",
(void*)android_view_ThreadedRenderer_setRtAnimationsEnabled},
+ {"nNotifyCallbackPending", "(J)V",
+ (void*)android_view_ThreadedRenderer_notifyCallbackPending},
};
static JavaVM* mJvm = nullptr;
@@ -1034,6 +1053,11 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
gFrameCompleteCallback.onFrameComplete =
GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
+ jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest");
+ gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V");
+ gCopyRequest.getDestinationBitmap =
+ GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J");
+
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 09be630dc741..f17129c8d953 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -22,7 +22,10 @@
#include "SkFont.h"
#include "SkFontMetrics.h"
#include "SkFontMgr.h"
+#include "SkRect.h"
#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkStream.h"
#include "SkTypeface.h"
#include "GraphicsJNI.h"
#include <nativehelper/ScopedUtfChars.h>
@@ -225,7 +228,7 @@ static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
std::string path = std::string(reader.readString());
if (path.empty()) {
return nullptr;
@@ -267,7 +270,7 @@ static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
return reader.read<int>();
} else {
@@ -280,7 +283,7 @@ static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
return reader.readArray<minikin::FontVariation>().second;
@@ -295,7 +298,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
minikin::FontVariation var;
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
var = reader.readArray<minikin::FontVariation>().first[index];
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index b68213549938..897c4d71c0d5 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -57,7 +57,8 @@ 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) {
+ jstring langTags, jint variant, jboolean isCustomFallback,
+ jboolean isDefaultFallback) {
std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
uint32_t localeId;
if (langTags == nullptr) {
@@ -66,9 +67,9 @@ static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderP
ScopedUtfChars str(env, langTags);
localeId = minikin::registerLocaleList(str.c_str());
}
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
+ std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
- isCustomFallback);
+ isCustomFallback, isDefaultFallback);
if (family->getCoverage().length() == 0) {
// No coverage means minikin rejected given font for some reasons.
jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -116,10 +117,10 @@ 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;IZ)J", (void*) FontFamily_Builder_build },
- { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+ {"nInitBuilder", "()J", (void*)FontFamily_Builder_initBuilder},
+ {"nAddFont", "(JJ)V", (void*)FontFamily_Builder_addFont},
+ {"nBuild", "(JLjava/lang/String;IZZ)J", (void*)FontFamily_Builder_build},
+ {"nGetReleaseNativeFamily", "()J", (void*)FontFamily_Builder_GetReleaseFunc},
};
static const JNINativeMethod gFontFamilyMethods[] = {
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 3f89c0712407..6a052dbb7cea 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -19,6 +19,8 @@
#include "RenderNode.h"
#include "SkiaDisplayList.h"
+class SkRRect;
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h
index 92c6f7721a08..d0e1ca35049a 100644
--- a/libs/hwui/pipeline/skia/HolePunch.h
+++ b/libs/hwui/pipeline/skia/HolePunch.h
@@ -17,7 +17,6 @@
#pragma once
#include <string>
-#include "SkRRect.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3ba540921f64..99f54c19d2e5 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -25,6 +25,7 @@
#include "SkColorFilter.h"
#include "SkRuntimeEffect.h"
#include "SkSurface.h"
+#include "Tonemapper.h"
#include "gl/GrGLTypes.h"
#include "math/mat4.h"
#include "system/graphics-base-v1.0.h"
@@ -76,37 +77,6 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons
isIntegerAligned(dstDevRect.y()));
}
-static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
- const shaders::LinearEffect& linearEffect,
- float maxDisplayLuminance,
- float currentDisplayLuminanceNits,
- float maxLuminance) {
- auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
- auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
- if (!runtimeEffect) {
- LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
- }
-
- SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
-
- effectBuilder.child("child") = std::move(shader);
-
- const auto uniforms = shaders::buildLinearEffectUniforms(
- linearEffect, mat4(), maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance);
-
- for (const auto& uniform : uniforms) {
- effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
- }
-
- return effectBuilder.makeShader();
-}
-
-static bool isHdrDataspace(ui::Dataspace dataspace) {
- const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
-
- return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
-}
-
static void adjustCropForYUV(uint32_t format, int bufferWidth, int bufferHeight, SkRect* cropRect) {
// Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
// a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
@@ -215,31 +185,10 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- const auto sourceDataspace = static_cast<ui::Dataspace>(
- ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
- const SkImageInfo& imageInfo = canvas->imageInfo();
- const auto destinationDataspace = static_cast<ui::Dataspace>(
- ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
-
- if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
- const auto effect = shaders::LinearEffect{
- .inputDataspace = sourceDataspace,
- .outputDataspace = destinationDataspace,
- .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
- .fakeInputDataspace = destinationDataspace};
- auto shader = layerImage->makeShader(sampling,
- SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
- constexpr float kMaxDisplayBrightess = 1000.f;
- constexpr float kCurrentDisplayBrightness = 500.f;
- shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
- kCurrentDisplayBrightness,
- layer->getMaxLuminanceNits());
- paint.setShader(shader);
- canvas->drawRect(skiaDestRect, paint);
- } else {
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- constraint);
- }
+ tonemapPaint(layerImage->imageInfo(), canvas->imageInfo(), layer->getMaxLuminanceNits(),
+ paint);
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+ constraint);
canvas->restore();
// restore the original matrix
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 9e17b9e6d985..1a47db5c8ec2 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -15,7 +15,11 @@
*/
#include "RenderNodeDrawable.h"
+#include <SkPaint.h>
#include <SkPaintFilterCanvas.h>
+#include <SkPoint.h>
+#include <SkRRect.h>
+#include <SkRect.h>
#include <gui/TraceUtils.h>
#include "RenderNode.h"
#include "SkiaDisplayList.h"
@@ -197,6 +201,7 @@ protected:
paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha);
return true;
}
+
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// We unroll the drawable using "this" canvas, so that draw calls contained inside will
// get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll.
@@ -288,7 +293,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
// with the same canvas transformation + clip into the target
// canvas then draw the layer on top
if (renderNode->hasHolePunches()) {
- TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);
+ TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut);
displayList->draw(&transformCanvas);
}
canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 6c390c3fce24..c7582e734009 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -18,6 +18,7 @@
#include "SkiaUtils.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkDrawable.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 7cfccb56382c..11977bd54c2c 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -19,8 +19,15 @@
#include "SkiaDisplayList.h"
#include "LightingInfo.h"
+#include <SkColor.h>
+#include <SkMatrix.h>
+#include <SkPath.h>
#include <SkPathOps.h>
+#include <SkPoint3.h>
+#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.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 90c4440c8339..a55de95035a7 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -16,6 +16,7 @@
#include "ShaderCache.h"
#include <GrDirectContext.h>
+#include <SkData.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <openssl/sha.h>
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 3e0fd5164011..bc35fa5f9987 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -17,12 +17,15 @@
#pragma once
#include <GrContextOptions.h>
+#include <SkRefCnt.h>
#include <cutils/compiler.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
+class SkData;
+
namespace android {
class BlobCache;
@@ -45,7 +48,7 @@ public:
* and puts the ShaderCache into an initialized state, such that it is
* able to insert and retrieve entries from the cache. If identity is
* non-null and validation fails, the cache is initialized but contains
- * no data. If size is less than zero, the cache is initilaized but
+ * no data. If size is less than zero, the cache is initialized but
* contains no data.
*
* This should be called when HWUI pipeline is initialized. When not in
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 8e350d5012a5..202a62cf320c 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -55,7 +55,9 @@ SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
// In case the surface was destroyed (e.g. a previous trimMemory call) we
// need to recreate it here.
- if (!isSurfaceReady() && mNativeWindow) {
+ if (mHardwareBuffer) {
+ mRenderThread.requireGlContext();
+ } else if (!isSurfaceReady() && mNativeWindow) {
setSurface(mNativeWindow.get(), mSwapBehavior);
}
@@ -67,17 +69,24 @@ MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
}
Frame SkiaOpenGLPipeline::getFrame() {
- LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
- "drawRenderNode called on a context with no surface!");
- return mEglManager.beginFrame(mEglSurface);
+ if (mHardwareBuffer) {
+ AHardwareBuffer_Desc description;
+ AHardwareBuffer_describe(mHardwareBuffer, &description);
+ return Frame(description.width, description.height, 0);
+ } else {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
+ }
}
IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) {
- if (!isCapturingSkp()) {
+ const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler,
+ const HardwareBufferRenderParams& bufferParams) {
+ if (!isCapturingSkp() && !mHardwareBuffer) {
mEglManager.damageFrame(frame, dirty);
}
@@ -104,19 +113,31 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
- sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
- mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
- mSurfaceColorSpace, &props));
+ sk_sp<SkSurface> surface;
+ SkMatrix preTransform;
+ if (mHardwareBuffer) {
+ surface = getBufferSkSurface(bufferParams);
+ preTransform = bufferParams.getTransform();
+ } else {
+ surface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT,
+ getSurfaceOrigin(), colorType,
+ mSurfaceColorSpace, &props);
+ preTransform = SkMatrix::I();
+ }
- LightingInfo::updateLighting(lightGeometry, lightInfo);
+ SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y);
+ LightGeometry localGeometry = lightGeometry;
+ localGeometry.center.x = lightCenter.fX;
+ localGeometry.center.y = lightCenter.fY;
+ LightingInfo::updateLighting(localGeometry, lightInfo);
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
- SkMatrix::I());
+ preTransform);
// Draw visual debugging features
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = surface->getCanvas();
- SkiaProfileRenderer profileRenderer(profileCanvas);
+ SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
profiler->draw(profileRenderer);
}
@@ -142,6 +163,10 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect
// metrics the frame was swapped at this point
currentFrameInfo->markSwapBuffers();
+ if (mHardwareBuffer) {
+ return false;
+ }
+
*requireSwap = drew || mEglManager.damageRequiresSwap();
if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
@@ -197,6 +222,26 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh
return false;
}
+[[nodiscard]] android::base::unique_fd SkiaOpenGLPipeline::flush() {
+ int fence = -1;
+ EGLSyncKHR sync = EGL_NO_SYNC_KHR;
+ mEglManager.createReleaseFence(true, &sync, &fence);
+ // If a sync object is returned here then the device does not support native
+ // fences, we block on the returned sync and return -1 as a file descriptor
+ if (sync != EGL_NO_SYNC_KHR) {
+ EGLDisplay display = mEglManager.eglDisplay();
+ EGLint result = eglClientWaitSyncKHR(display, sync, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x",
+ eglGetError());
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence");
+ }
+ eglDestroySyncKHR(display, sync);
+ }
+ return android::base::unique_fd(fence);
+}
+
bool SkiaOpenGLPipeline::isSurfaceReady() {
return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index a80c613697f2..940d6bfdb83c 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -21,6 +21,7 @@
#include "SkiaPipeline.h"
#include "renderstate/RenderState.h"
+#include "renderthread/HardwareBufferRenderParams.h"
namespace android {
@@ -36,19 +37,18 @@ public:
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
- renderthread::IRenderPipeline::DrawResult draw(const renderthread::Frame& frame,
- const SkRect& screenDirty, const SkRect& dirty,
- const LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
- const LightInfo& lightInfo,
- const std::vector<sp<RenderNode> >& renderNodes,
- FrameInfoVisualizer* profiler) override;
+ renderthread::IRenderPipeline::DrawResult draw(
+ const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
+ const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
+ const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
+ [[nodiscard]] android::base::unique_fd flush() override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index bc386feb2d6f..1a336c5855d9 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -16,15 +16,25 @@
#include "SkiaPipeline.h"
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkColorSpace.h>
+#include <SkData.h>
+#include <SkImage.h>
#include <SkImageEncoder.h>
#include <SkImageInfo.h>
#include <SkImagePriv.h>
+#include <SkMatrix.h>
#include <SkMultiPictureDocument.h>
#include <SkOverdrawCanvas.h>
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
#include <SkSerialProcs.h>
+#include <SkStream.h>
+#include <SkString.h>
#include <SkTypeface.h>
#include <android-base/properties.h>
#include <unistd.h>
@@ -594,6 +604,31 @@ void SkiaPipeline::dumpResourceCacheUsage() const {
ALOGD("%s", log.c_str());
}
+void SkiaPipeline::setHardwareBuffer(AHardwareBuffer* buffer) {
+ if (mHardwareBuffer) {
+ AHardwareBuffer_release(mHardwareBuffer);
+ mHardwareBuffer = nullptr;
+ }
+
+ if (buffer) {
+ AHardwareBuffer_acquire(buffer);
+ mHardwareBuffer = buffer;
+ }
+}
+
+sk_sp<SkSurface> SkiaPipeline::getBufferSkSurface(
+ const renderthread::HardwareBufferRenderParams& bufferParams) {
+ auto bufferColorSpace = bufferParams.getColorSpace();
+ if (mBufferSurface == nullptr || mBufferColorSpace == nullptr ||
+ !SkColorSpace::Equals(mBufferColorSpace.get(), bufferColorSpace.get())) {
+ mBufferSurface = SkSurface::MakeFromAHardwareBuffer(
+ mRenderThread.getGrContext(), mHardwareBuffer, kTopLeft_GrSurfaceOrigin,
+ bufferColorSpace, nullptr, true);
+ mBufferColorSpace = bufferColorSpace;
+ }
+ return mBufferSurface;
+}
+
void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
mColorMode = colorMode;
switch (colorMode) {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index bc8a5659dd83..4f9334654c9b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -16,14 +16,18 @@
#pragma once
-#include <SkSurface.h>
+#include <SkColorSpace.h>
#include <SkDocument.h>
#include <SkMultiPictureDocument.h>
+#include <SkSurface.h>
+
#include "Lighting.h"
#include "hwui/AnimatedImageDrawable.h"
#include "renderthread/CanvasContext.h"
+#include "renderthread/HardwareBufferRenderParams.h"
#include "renderthread/IRenderPipeline.h"
+class SkFILEWStream;
class SkPictureRecorder;
struct SkSharingSerialContext;
@@ -71,11 +75,20 @@ public:
mCaptureMode = callback ? CaptureMode::CallbackAPI : CaptureMode::None;
}
+ virtual void setHardwareBuffer(AHardwareBuffer* buffer) override;
+ bool hasHardwareBuffer() override { return mHardwareBuffer != nullptr; }
+
protected:
+ sk_sp<SkSurface> getBufferSkSurface(
+ const renderthread::HardwareBufferRenderParams& bufferParams);
void dumpResourceCacheUsage() const;
renderthread::RenderThread& mRenderThread;
+ AHardwareBuffer* mHardwareBuffer = nullptr;
+ sk_sp<SkSurface> mBufferSurface = nullptr;
+ sk_sp<SkColorSpace> mBufferColorSpace = nullptr;
+
ColorMode mColorMode = ColorMode::Default;
SkColorType mSurfaceColorType;
sk_sp<SkColorSpace> mSurfaceColorSpace;
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
index 492c39f1288c..81cfc5d93419 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
@@ -33,13 +33,5 @@ void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint
}
}
-uint32_t SkiaProfileRenderer::getViewportWidth() {
- return mCanvas->imageInfo().width();
-}
-
-uint32_t SkiaProfileRenderer::getViewportHeight() {
- return mCanvas->imageInfo().height();
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index dc8420f4e01b..96d2a5e58139 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,18 +23,21 @@ namespace uirenderer {
class SkiaProfileRenderer : public IProfileRenderer {
public:
- explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
+ explicit SkiaProfileRenderer(SkCanvas* canvas, uint32_t width, uint32_t height)
+ : mCanvas(canvas), mWidth(width), mHeight(height) {}
void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
void drawRects(const float* rects, int count, const SkPaint& paint) override;
- uint32_t getViewportWidth() override;
- uint32_t getViewportHeight() override;
+ uint32_t getViewportWidth() override { return mWidth; }
+ uint32_t getViewportHeight() override { return mHeight; }
virtual ~SkiaProfileRenderer() {}
private:
// Does not have ownership.
SkCanvas* mCanvas;
+ uint32_t mWidth;
+ uint32_t mHeight;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 9c51e628e04a..1f87865f2672 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -16,7 +16,20 @@
#include "SkiaRecordingCanvas.h"
#include "hwui/Paint.h"
+#include <include/private/SkTemplates.h> // SkAutoSTMalloc
+#include <SkBlendMode.h>
+#include <SkData.h>
+#include <SkDrawable.h>
+#include <SkImage.h>
#include <SkImagePriv.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkRRect.h>
+#include <SkSamplingOptions.h>
+#include <SkTypes.h>
#include "CanvasTransform.h"
#ifdef __ANDROID__ // Layoutlib does not support Layers
#include "Layer.h"
@@ -56,20 +69,22 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in
mDisplayList->setHasHolePunches(false);
}
-void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
- // Add the marker annotation to allow HWUI to determine where the current
- // clip/transformation should be applied
+void SkiaRecordingCanvas::punchHole(const SkRRect& rect, float alpha) {
+ // Add the marker annotation to allow HWUI to determine the current
+ // clip/transformation and alpha should be applied
SkVector vector = rect.getSimpleRadii();
- float data[2];
+ float data[3];
data[0] = vector.x();
data[1] = vector.y();
+ data[2] = alpha;
mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
- SkData::MakeWithCopy(data, 2 * sizeof(float)));
+ SkData::MakeWithCopy(data, sizeof(data)));
// Clear the current rect within the layer itself
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mRecorder.drawRRect(rect, paint);
mDisplayList->setHasHolePunches(true);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 1445a27e4248..7844e2cc2a73 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -22,6 +22,11 @@
#include "SkiaDisplayList.h"
#include "pipeline/skia/AnimatedDrawables.h"
+class SkBitmap;
+class SkMatrix;
+class SkPaint;
+class SkRRect;
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -45,7 +50,7 @@ public:
initDisplayList(renderNode, width, height);
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void finishRecording(uirenderer::RenderNode* destination) override;
std::unique_ptr<SkiaDisplayList> finishRecording();
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index cc2565d88d5e..b94b6cf0546a 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -57,43 +57,63 @@ VulkanManager& SkiaVulkanPipeline::vulkanManager() {
MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
// In case the surface was destroyed (e.g. a previous trimMemory call) we
// need to recreate it here.
- if (!isSurfaceReady() && mNativeWindow) {
+ if (mHardwareBuffer) {
+ mRenderThread.requireVkContext();
+ } else if (!isSurfaceReady() && mNativeWindow) {
setSurface(mNativeWindow.get(), SwapBehavior::kSwap_default);
}
return isContextReady() ? MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed;
}
Frame SkiaVulkanPipeline::getFrame() {
- LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
- return vulkanManager().dequeueNextBuffer(mVkSurface);
+ if (mHardwareBuffer) {
+ AHardwareBuffer_Desc description;
+ AHardwareBuffer_describe(mHardwareBuffer, &description);
+ return Frame(description.width, description.height, 0);
+ } else {
+ LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
+ "getFrame() called on a context with no surface!");
+ return vulkanManager().dequeueNextBuffer(mVkSurface);
+ }
}
IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) {
- sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface();
+ const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler,
+ const HardwareBufferRenderParams& bufferParams) {
+ sk_sp<SkSurface> backBuffer;
+ SkMatrix preTransform;
+ if (mHardwareBuffer) {
+ backBuffer = getBufferSkSurface(bufferParams);
+ preTransform = bufferParams.getTransform();
+ } else {
+ backBuffer = mVkSurface->getCurrentSkSurface();
+ preTransform = mVkSurface->getCurrentPreTransform();
+ }
+
if (backBuffer.get() == nullptr) {
return {false, -1};
}
// update the coordinates of the global light position based on surface rotation
- SkPoint lightCenter = mVkSurface->getCurrentPreTransform().mapXY(lightGeometry.center.x,
- lightGeometry.center.y);
+ SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y);
LightGeometry localGeometry = lightGeometry;
localGeometry.center.x = lightCenter.fX;
localGeometry.center.y = lightCenter.fY;
LightingInfo::updateLighting(localGeometry, lightInfo);
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer,
- mVkSurface->getCurrentPreTransform());
+ preTransform);
// Draw visual debugging features
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = backBuffer->getCanvas();
- SkiaProfileRenderer profileRenderer(profileCanvas);
+ SkAutoCanvasRestore saver(profileCanvas, true);
+ profileCanvas->concat(mVkSurface->getCurrentPreTransform());
+ SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
profiler->draw(profileRenderer);
}
@@ -114,12 +134,16 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) {
- *requireSwap = drew;
-
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
currentFrameInfo->markSwapBuffers();
+ if (mHardwareBuffer) {
+ return false;
+ }
+
+ *requireSwap = drew;
+
if (*requireSwap) {
vulkanManager().swapBuffers(mVkSurface, screenDirty);
}
@@ -135,6 +159,12 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
void SkiaVulkanPipeline::onStop() {}
+[[nodiscard]] android::base::unique_fd SkiaVulkanPipeline::flush() {
+ int fence = -1;
+ vulkanManager().createReleaseFence(&fence, mRenderThread.getGrContext());
+ return android::base::unique_fd(fence);
+}
+
// We can safely ignore the swap behavior because VkManager will always operate
// in a mode equivalent to EGLManager::SwapBehavior::kBufferAge
bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior /*swapBehavior*/) {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index a6e685d08aeb..2c7b268e8174 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -16,11 +16,15 @@
#pragma once
+#include "SkRefCnt.h"
#include "SkiaPipeline.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/HardwareBufferRenderParams.h"
#include "renderthread/VulkanManager.h"
#include "renderthread/VulkanSurface.h"
-#include "renderstate/RenderState.h"
+class SkBitmap;
+struct SkRect;
namespace android {
namespace uirenderer {
@@ -33,18 +37,18 @@ public:
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
- renderthread::IRenderPipeline::DrawResult draw(const renderthread::Frame& frame,
- const SkRect& screenDirty, const SkRect& dirty,
- const LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
- const LightInfo& lightInfo,
- const std::vector<sp<RenderNode> >& renderNodes,
- FrameInfoVisualizer* profiler) override;
+ renderthread::IRenderPipeline::DrawResult draw(
+ const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
+ const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
+ const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
+ [[nodiscard]] android::base::unique_fd flush() override;
+
bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
void onStop() override;
bool isSurfaceReady() override;
@@ -59,7 +63,6 @@ protected:
private:
renderthread::VulkanManager& vulkanManager();
-
renderthread::VulkanSurface* mVkSurface = nullptr;
sp<ANativeWindow> mNativeWindow;
};
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2dbeb3adfab3..b169c9200e88 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
#include "StretchMask.h"
-#include "SkSurface.h"
+
+#include "SkBlendMode.h"
#include "SkCanvas.h"
+#include "SkSurface.h"
#include "TransformCanvas.h"
#include "SkiaDisplayList.h"
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 41e36874b862..c320df035d08 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -19,19 +19,25 @@
#include "HolePunch.h"
#include "SkData.h"
#include "SkDrawable.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRect.h"
+#include "SkRRect.h"
using namespace android::uirenderer::skiapipeline;
void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
if (HOLE_PUNCH_ANNOTATION == key) {
auto* rectParams = reinterpret_cast<const float*>(value->data());
- float radiusX = rectParams[0];
- float radiusY = rectParams[1];
+ const float radiusX = rectParams[0];
+ const float radiusY = rectParams[1];
+ const float alpha = rectParams[2];
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
SkPaint paint;
paint.setColor(SkColors::kBlack);
paint.setBlendMode(mHolePunchBlendMode);
+ paint.setAlphaf(alpha);
mWrappedCanvas->drawRRect(roundRect, paint);
}
}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
index 685b71d017e9..15f0c1abc55a 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.h
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -19,6 +19,13 @@
#include "SkPaintFilterCanvas.h"
#include <effects/StretchEffect.h>
+class SkData;
+class SkDrawable;
+class SkMatrix;
+class SkPaint;
+enum class SkBlendMode;
+struct SkRect;
+
class TransformCanvas : public SkPaintFilterCanvas {
public:
TransformCanvas(SkCanvas* target, SkBlendMode blendmode) :
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 3c7617d35c7c..e168a7b9459a 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -33,6 +33,8 @@
#include "thread/ThreadBase.h"
#include "utils/TimeUtils.h"
+#include <SkBlendMode.h>
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ded2b06fb3cf..1c7688464c27 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,6 +16,16 @@
#include "CacheManager.h"
+#include <GrContextOptions.h>
+#include <SkExecutor.h>
+#include <SkGraphics.h>
+#include <SkMathPriv.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+#include <set>
+
+#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "Layer.h"
#include "Properties.h"
@@ -25,40 +35,34 @@
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
-#include <utils/Trace.h>
-
-#include <GrContextOptions.h>
-#include <SkExecutor.h>
-#include <SkGraphics.h>
-#include <SkMathPriv.h>
-#include <math.h>
-#include <set>
namespace android {
namespace uirenderer {
namespace renderthread {
-// This multiplier was selected based on historical review of cache sizes relative
-// to the screen resolution. This is meant to be a conservative default based on
-// that analysis. The 4.0f is used because the default pixel format is assumed to
-// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
-#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
-
-CacheManager::CacheManager()
- : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight())
- , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER)
- , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE)
- // This sets the maximum size for a single texture atlas in the GPU font cache. If
- // necessary, the cache can allocate additional textures that are counted against the
- // total cache limits provided to Skia.
- , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea))
- // This sets the maximum size of the CPU font cache to be at least the same size as the
- // total number of GPU font caches (i.e. 4 separate GPU atlases).
- , mMaxCpuFontCacheBytes(
- std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit()))
- , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
+ mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
+ mMemoryPolicy.initialMaxSurfaceAreaScale);
+ setupCacheLimits();
+}
+
+void CacheManager::setupCacheLimits() {
+ mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
+ mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
+ // This sets the maximum size for a single texture atlas in the GPU font cache. If
+ // necessary, the cache can allocate additional textures that are counted against the
+ // total cache limits provided to Skia.
+ mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+ // This sets the maximum size of the CPU font cache to be at least the same size as the
+ // total number of GPU font caches (i.e. 4 separate GPU atlases).
+ mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
+ mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
+
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
+ if (mGrContext) {
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ }
}
void CacheManager::reset(sk_sp<GrDirectContext> context) {
@@ -69,6 +73,7 @@ void CacheManager::reset(sk_sp<GrDirectContext> context) {
if (context) {
mGrContext = std::move(context);
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
}
}
@@ -93,10 +98,9 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);
contextOptions->fPersistentCache = &cache;
- contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
-void CacheManager::trimMemory(TrimMemoryMode mode) {
+void CacheManager::trimMemory(TrimLevel mode) {
if (!mGrContext) {
return;
}
@@ -104,21 +108,28 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
// flush and submit all work to the gpu and wait for it to finish
mGrContext->flushAndSubmit(/*syncCpu=*/true);
+ if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
+ mode = TrimLevel::COMPLETE;
+ }
+
switch (mode) {
- case TrimMemoryMode::Complete:
+ case TrimLevel::COMPLETE:
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
+ mRenderThread.destroyRenderingContext();
break;
- case TrimMemoryMode::UiHidden:
+ case TrimLevel::UI_HIDDEN:
// Here we purge all the unlocked scratch resources and then toggle the resources cache
// limits between the background and max amounts. This causes the unlocked resources
// that have persistent data to be purged in LRU order.
- mGrContext->purgeUnlockedResources(true);
mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
- mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
+ mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
+ default:
+ break;
}
}
@@ -147,11 +158,29 @@ void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
}
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
+ log.appendFormat(R"(Memory policy:
+ Max surface area: %zu
+ Max resource usage: %.2fMB (x%.0f)
+ Background retention: %.0f%% (altUiHidden = %s)
+)",
+ mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
+ mMemoryPolicy.surfaceSizeMultiplier,
+ mMemoryPolicy.backgroundRetentionPercent * 100.0f,
+ mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
+ if (Properties::isSystemOrPersistent) {
+ log.appendFormat(" IsSystemOrPersistent\n");
+ }
+ log.appendFormat(" GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
+ size_t stoppedContexts = 0;
+ for (auto context : mCanvasContexts) {
+ if (context->isStopped()) stoppedContexts++;
+ }
+ log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
+
if (!mGrContext) {
- log.appendFormat("No valid cache instance.\n");
+ log.appendFormat("No GPU context.\n");
return;
}
-
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
@@ -199,6 +228,8 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
}
void CacheManager::onFrameCompleted() {
+ cancelDestroyContext();
+ mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
if (ATRACE_ENABLED()) {
static skiapipeline::ATraceMemoryDump tracer;
tracer.startFrame();
@@ -210,11 +241,82 @@ void CacheManager::onFrameCompleted() {
}
}
-void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
- if (mGrContext) {
- mGrContext->performDeferredCleanup(
- std::chrono::milliseconds(cleanupOlderThanMillis),
- /* scratchResourcesOnly */true);
+void CacheManager::onThreadIdle() {
+ if (!mGrContext || mFrameCompletions.size() == 0) return;
+
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ // Rate limiting
+ 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));
+ mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
+ mMemoryPolicy.purgeScratchOnly);
+ }
+}
+
+void CacheManager::scheduleDestroyContext() {
+ if (mMemoryPolicy.contextTimeout > 0) {
+ mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
+ [this, genId = mGenerationId] {
+ if (mGenerationId != genId) return;
+ // GenID should have already stopped this, but just in
+ // case
+ if (!areAllContextsStopped()) return;
+ mRenderThread.destroyRenderingContext();
+ });
+ }
+}
+
+void CacheManager::cancelDestroyContext() {
+ if (mIsDestructionPending) {
+ mIsDestructionPending = false;
+ mGenerationId++;
+ }
+}
+
+bool CacheManager::areAllContextsStopped() {
+ for (auto context : mCanvasContexts) {
+ if (!context->isStopped()) return false;
+ }
+ return true;
+}
+
+void CacheManager::checkUiHidden() {
+ if (!mGrContext) return;
+
+ if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
+ trimMemory(TrimLevel::UI_HIDDEN);
+ }
+}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {
+ mCanvasContexts.push_back(context);
+ cancelDestroyContext();
+}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {
+ std::erase(mCanvasContexts, context);
+ checkUiHidden();
+ if (mCanvasContexts.empty()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::onContextStopped(CanvasContext* context) {
+ checkUiHidden();
+ if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {
+ int frameArea = width * height;
+ if (frameArea > mMaxSurfaceArea) {
+ mMaxSurfaceArea = frameArea;
+ setupCacheLimits();
}
}
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index af82672c6f23..d21ac9badc43 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -22,7 +22,11 @@
#endif
#include <SkSurface.h>
#include <utils/String8.h>
+
#include <vector>
+
+#include "MemoryPolicy.h"
+#include "utils/RingBuffer.h"
#include "utils/TimeUtils.h"
namespace android {
@@ -35,17 +39,15 @@ class RenderState;
namespace renderthread {
-class IRenderPipeline;
class RenderThread;
+class CanvasContext;
class CacheManager {
public:
- enum class TrimMemoryMode { Complete, UiHidden };
-
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
#endif
- void trimMemory(TrimMemoryMode mode);
+ void trimMemory(TrimLevel mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
@@ -53,30 +55,50 @@ public:
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
void onFrameCompleted();
+ void notifyNextFrameSize(int width, int height);
+
+ void onThreadIdle();
- void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
+ void registerCanvasContext(CanvasContext* context);
+ void unregisterCanvasContext(CanvasContext* context);
+ void onContextStopped(CanvasContext* context);
private:
friend class RenderThread;
- explicit CacheManager();
+ explicit CacheManager(RenderThread& thread);
+ void setupCacheLimits();
+ bool areAllContextsStopped();
+ void checkUiHidden();
+ void scheduleDestroyContext();
+ void cancelDestroyContext();
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void reset(sk_sp<GrDirectContext> grContext);
#endif
void destroy();
- const size_t mMaxSurfaceArea;
+ RenderThread& mRenderThread;
+ const MemoryPolicy& mMemoryPolicy;
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
sk_sp<GrDirectContext> mGrContext;
#endif
- const size_t mMaxResourceBytes;
- const size_t mBackgroundResourceBytes;
+ size_t mMaxSurfaceArea = 0;
+
+ size_t mMaxResourceBytes = 0;
+ size_t mBackgroundResourceBytes = 0;
+
+ size_t mMaxGpuFontAtlasBytes = 0;
+ size_t mMaxCpuFontCacheBytes = 0;
+ size_t mBackgroundCpuFontCacheBytes = 0;
+
+ std::vector<CanvasContext*> mCanvasContexts;
+ RingBuffer<uint64_t, 100> mFrameCompletions;
- const size_t mMaxGpuFontAtlasBytes;
- const size_t mMaxCpuFontCacheBytes;
- const size_t mBackgroundCpuFontCacheBytes;
+ nsecs_t mLastDeferredCleanup = 0;
+ bool mIsDestructionPending = false;
+ uint32_t mGenerationId = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75d3ff7753cb..b769f8d15044 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,9 +42,6 @@
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
-#define TRIM_MEMORY_COMPLETE 80
-#define TRIM_MEMORY_UI_HIDDEN 20
-
#define LOG_FRAMETIME_MMA 0
#if LOG_FRAMETIME_MMA
@@ -74,16 +71,19 @@ CanvasContext* ScopedActiveContext::sActiveContext = nullptr;
} /* namespace */
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
- RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+ RenderNode* rootRenderNode, IContextFactory* contextFactory,
+ int32_t uiThreadId, int32_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+ uiThreadId, renderThreadId);
case RenderPipelineType::SkiaVulkan:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+ uiThreadId, renderThreadId);
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -113,7 +113,8 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory,
- std::unique_ptr<IRenderPipeline> renderPipeline)
+ std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+ pid_t renderThreadId)
: mRenderThread(thread)
, mGenerationID(0)
, mOpaque(!translucent)
@@ -121,7 +122,9 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mJankTracker(&thread.globalProfileData())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
- , mRenderPipeline(std::move(renderPipeline)) {
+ , mRenderPipeline(std::move(renderPipeline))
+ , mHintSessionWrapper(uiThreadId, renderThreadId) {
+ mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
@@ -133,6 +136,7 @@ CanvasContext::~CanvasContext() {
node->clearRoot();
}
mRenderNodes.clear();
+ mRenderThread.cacheManager().unregisterCanvasContext(this);
}
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -149,11 +153,13 @@ void CanvasContext::removeRenderNode(RenderNode* node) {
void CanvasContext::destroy() {
stopDrawing();
+ setHardwareBuffer(nullptr);
setSurface(nullptr);
setSurfaceControl(nullptr);
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
+ mRenderThread.cacheManager().onContextStopped(this);
}
static void setBufferCount(ANativeWindow* window) {
@@ -171,6 +177,19 @@ static void setBufferCount(ANativeWindow* window) {
native_window_set_buffer_count(window, bufferCount);
}
+void CanvasContext::setHardwareBuffer(AHardwareBuffer* buffer) {
+ if (mHardwareBuffer) {
+ AHardwareBuffer_release(mHardwareBuffer);
+ mHardwareBuffer = nullptr;
+ }
+
+ if (buffer) {
+ AHardwareBuffer_acquire(buffer);
+ mHardwareBuffer = buffer;
+ }
+ mRenderPipeline->setHardwareBuffer(mHardwareBuffer);
+}
+
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
@@ -251,7 +270,8 @@ void CanvasContext::setStopped(bool stopped) {
mGenerationID++;
mRenderThread.removeFrameCallback(this);
mRenderPipeline->onStop();
- } else if (mIsDirty && hasSurface()) {
+ mRenderThread.cacheManager().onContextStopped(this);
+ } else if (mIsDirty && hasOutputTarget()) {
mRenderThread.postFrameCallback(this);
}
}
@@ -384,7 +404,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
mIsDirty = true;
- if (CC_UNLIKELY(!hasSurface())) {
+ if (CC_UNLIKELY(!hasOutputTarget())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
info.out.canDrawThisFrame = false;
return;
@@ -461,7 +481,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
}
void CanvasContext::stopDrawing() {
- cleanupResources();
mRenderThread.removeFrameCallback(this);
mAnimationContext->pauseAnimators();
mGenerationID++;
@@ -470,18 +489,25 @@ void CanvasContext::stopDrawing() {
void CanvasContext::notifyFramePending() {
ATRACE_CALL();
mRenderThread.pushBackFrameCallback(this);
+ sendLoadResetHint();
}
-nsecs_t CanvasContext::draw() {
+void CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return 0;
+ return;
}
}
SkRect dirty;
mDamageAccumulator.finish(&dirty);
+ // reset syncDelayDuration each time we draw
+ nsecs_t syncDelayDuration = mSyncDelayDuration;
+ nsecs_t idleDuration = mIdleDuration;
+ mSyncDelayDuration = 0;
+ mIdleDuration = 0;
+
if (!Properties::isDrawingEnabled() ||
(dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +524,7 @@ nsecs_t CanvasContext::draw() {
std::invoke(func, false /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
- return 0;
+ return;
}
ScopedActiveContext activeContext(this);
@@ -523,7 +549,7 @@ nsecs_t CanvasContext::draw() {
std::scoped_lock lock(mFrameMetricsReporterMutex);
drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
&mLayerUpdateQueue, mContentDrawBounds, mOpaque,
- mLightInfo, mRenderNodes, &(profiler()));
+ mLightInfo, mRenderNodes, &(profiler()), mBufferParams);
}
uint64_t frameCompleteNr = getFrameNumber();
@@ -543,6 +569,8 @@ nsecs_t CanvasContext::draw() {
}
bool requireSwap = false;
+ bool didDraw = false;
+
int error = OK;
bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
mCurrentFrameInfo, &requireSwap);
@@ -553,7 +581,7 @@ nsecs_t CanvasContext::draw() {
mIsDirty = false;
if (requireSwap) {
- bool didDraw = true;
+ didDraw = true;
// Handle any swapchain errors
error = mNativeSurface->getAndClearError();
if (error == TIMED_OUT) {
@@ -648,23 +676,25 @@ nsecs_t CanvasContext::draw() {
}
}
- cleanupResources();
- mRenderThread.cacheManager().onFrameCompleted();
- return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
-}
+ int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+ int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+ int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+ mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
-void CanvasContext::cleanupResources() {
- auto& tracker = mJankTracker.frames();
- auto size = tracker.size();
- auto capacity = tracker.capacity();
- if (size == capacity) {
- nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t frameCompleteNanos =
- tracker[0].get(FrameInfoIndex::FrameCompleted);
- nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
- nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
- mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
+ if (didDraw) {
+ int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration - idleDuration;
+ mHintSessionWrapper.reportActualWorkDuration(actualDuration);
}
+
+ mLastDequeueBufferDuration = dequeueBufferDuration;
+
+ mRenderThread.cacheManager().onFrameCompleted();
+ return;
}
void CanvasContext::reportMetricsWithPresentTime() {
@@ -777,6 +807,8 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
+ mIdleDuration =
+ systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
prepareAndDraw(nullptr);
}
@@ -790,6 +822,7 @@ SkISize CanvasContext::getNextFrameSize() const {
SkISize size;
size.fWidth = ANativeWindow_getWidth(anw);
size.fHeight = ANativeWindow_getHeight(anw);
+ mRenderThread.cacheManager().notifyNextFrameSize(size.fWidth, size.fHeight);
return size;
}
@@ -868,18 +901,6 @@ void CanvasContext::destroyHardwareResources() {
}
}
-void CanvasContext::trimMemory(RenderThread& thread, int level) {
- ATRACE_CALL();
- if (!thread.getGrContext()) return;
- ATRACE_CALL();
- if (level >= TRIM_MEMORY_COMPLETE) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.destroyRenderingContext();
- } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
- }
-}
-
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
return mRenderPipeline->createTextureLayer();
}
@@ -996,6 +1017,14 @@ void CanvasContext::prepareSurfaceControlForWebview() {
}
}
+void CanvasContext::sendLoadResetHint() {
+ mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+ mSyncDelayDuration = duration;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 951ee216ce35..3f796d9b7b65 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,10 +16,26 @@
#pragma once
+#include <SkBitmap.h>
+#include <SkRect.h>
+#include <SkSize.h>
+#include <cutils/compiler.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+
+#include <functional>
+#include <future>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ColorMode.h"
#include "DamageAccumulator.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
#include "IContextFactory.h"
#include "IRenderPipeline.h"
#include "JankTracker.h"
@@ -30,21 +46,6 @@
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
-#include <SkBitmap.h>
-#include <SkRect.h>
-#include <SkSize.h>
-#include <cutils/compiler.h>
-#include <utils/Functor.h>
-#include <utils/Mutex.h>
-
-#include <functional>
-#include <future>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
namespace android {
namespace uirenderer {
@@ -66,7 +67,8 @@ class Frame;
class CanvasContext : public IFrameCallback {
public:
static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory);
+ IContextFactory* contextFactory, pid_t uiThreadId,
+ pid_t renderThreadId);
virtual ~CanvasContext();
/**
@@ -123,11 +125,13 @@ public:
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
+ void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
void setSurfaceControl(ASurfaceControl* surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
- bool hasSurface() const { return mNativeSurface.get(); }
+ bool isStopped() { return mStopped || !hasOutputTarget(); }
+ bool hasOutputTarget() const { return mNativeSurface.get() || mHardwareBuffer; }
void allocateBuffers();
void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
@@ -137,7 +141,7 @@ public:
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
// Returns the DequeueBufferDuration.
- nsecs_t draw();
+ void draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
@@ -148,7 +152,6 @@ public:
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
- static void trimMemory(RenderThread& thread, int level);
DeferredLayerUpdater* createTextureLayer();
@@ -204,6 +207,10 @@ public:
mASurfaceTransactionCallback = callback;
}
+ void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
+ mBufferParams = params;
+ }
+
bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
@@ -214,9 +221,14 @@ public:
static CanvasContext* getActiveContext();
+ void sendLoadResetHint();
+
+ void setSyncDelayDuration(nsecs_t duration);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+ IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+ pid_t uiThreadId, pid_t renderThreadId);
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -251,6 +263,9 @@ private:
int32_t mLastFrameHeight = 0;
RenderThread& mRenderThread;
+
+ AHardwareBuffer* mHardwareBuffer = nullptr;
+ HardwareBufferRenderParams mBufferParams;
std::unique_ptr<ReliableSurface> mNativeSurface;
// The SurfaceControl reference is passed from ViewRootImpl, can be set to
// NULL to remove the reference
@@ -331,7 +346,10 @@ private:
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
- void cleanupResources();
+ HintSessionWrapper mHintSessionWrapper;
+ nsecs_t mLastDequeueBufferDuration = 0;
+ nsecs_t mSyncDelayDuration = 0;
+ nsecs_t mIdleDuration = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 03f02de98efe..b06c5dd9ad36 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,9 +16,9 @@
#include "DrawFrameTask.h"
-#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
+
#include <algorithm>
#include "../DeferredLayerUpdater.h"
@@ -26,64 +26,13 @@
#include "../Properties.h"
#include "../RenderNode.h"
#include "CanvasContext.h"
+#include "HardwareBufferRenderParams.h"
#include "RenderThread.h"
-#include "thread/CommonPool.h"
namespace android {
namespace uirenderer {
namespace renderthread {
-namespace {
-
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
-
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
-
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
-
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPerformanceHintBindingInitialized = true;
-}
-
-} // namespace
-
DrawFrameTask::DrawFrameTask()
: mRenderThread(nullptr)
, mContext(nullptr)
@@ -92,13 +41,11 @@ DrawFrameTask::DrawFrameTask()
DrawFrameTask::~DrawFrameTask() {}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
- mUiThreadId = uiThreadId;
- mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -142,8 +89,12 @@ void DrawFrameTask::postAndWait() {
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
- nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+ mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
+
+ auto hardwareBufferParams = mHardwareBufferParams;
+ mContext->setHardwareBufferRenderParams(hardwareBufferParams);
+ IRenderPipeline* pipeline = mContext->getRenderPipeline();
bool canUnblockUiThread;
bool canDrawThisFrame;
{
@@ -166,9 +117,6 @@ void DrawFrameTask::run() {
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
- int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
- int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
- int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
@@ -179,16 +127,15 @@ void DrawFrameTask::run() {
if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
frameNr = context->getFrameNumber()]() {
- auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ auto frameCommitCallback = frameCallback(syncResult, frameNr);
if (frameCommitCallback) {
context->addFrameCommitListener(std::move(frameCommitCallback));
}
});
}
- nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- dequeueBufferDuration = context->draw();
+ context->draw();
} else {
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -208,26 +155,10 @@ void DrawFrameTask::run() {
unblockUiThread();
}
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
- constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
- constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
- int64_t targetWorkDuration = frameDeadline - intendedVsync;
- targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
- if (targetWorkDuration > kSanityCheckLowerBound &&
- targetWorkDuration < kSanityCheckUpperBound &&
- targetWorkDuration != mLastTargetWorkDuration) {
- mLastTargetWorkDuration = targetWorkDuration;
- mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
- }
- int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- int64_t actualDuration = frameDuration -
- (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
- dequeueBufferDuration;
- if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
- mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+ if (pipeline->hasHardwareBuffer()) {
+ auto fence = pipeline->flush();
+ hardwareBufferParams.invokeRenderCallback(std::move(fence), 0);
}
-
- mLastDequeueBufferDuration = dequeueBufferDuration;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -253,8 +184,9 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
- if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
- if (!mContext->hasSurface()) {
+ bool hasTarget = mContext->hasOutputTarget();
+ if (CC_UNLIKELY(!hasTarget || !canDraw)) {
+ if (!hasTarget) {
mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
} else {
// If we have a surface but can't draw we must be stopped
@@ -280,44 +212,6 @@ void DrawFrameTask::unblockUiThread() {
mSignal.signal();
}
-DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
- if (!Properties::useHintManager) return;
- if (uiThreadId < 0 || renderThreadId < 0) return;
-
- ensureAPerformanceHintBindingInitialized();
-
- APerformanceHintManager* manager = gAPH_getManagerFn();
- if (!manager) return;
-
- std::vector<int32_t> tids = CommonPool::getThreadIds();
- tids.push_back(uiThreadId);
- tids.push_back(renderThreadId);
-
- // DrawFrameTask code will always set a target duration before reporting actual durations.
- // So this is just a placeholder value that's never used.
- int64_t dummyTargetDurationNanos = 16666667;
- mHintSession =
- gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
-}
-
-DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
- if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
- if (mHintSession) {
- gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
- if (mHintSession) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
- }
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index d6fc292d5900..c5c5fe280743 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -28,8 +27,16 @@
#include "../Rect.h"
#include "../TreeInfo.h"
#include "RenderTask.h"
+#include "SkColorSpace.h"
+#include "SwapBehavior.h"
+#include "utils/TimeUtils.h"
+#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
+#include <android/hardware_buffer.h>
+#endif
+#include "HardwareBufferRenderParams.h"
namespace android {
+
namespace uirenderer {
class DeferredLayerUpdater;
@@ -61,8 +68,7 @@ public:
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -90,19 +96,11 @@ public:
void forceDrawNextFrame() { mForceDrawFrame = true; }
-private:
- class HintSessionWrapper {
- public:
- HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
- ~HintSessionWrapper();
-
- void updateTargetWorkDuration(long targetDurationNanos);
- void reportActualWorkDuration(long actualDurationNanos);
-
- private:
- APerformanceHintSession* mHintSession = nullptr;
- };
+ void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
+ mHardwareBufferParams = params;
+ }
+private:
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -113,8 +111,6 @@ private:
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
- int32_t mUiThreadId = -1;
- int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -127,14 +123,11 @@ private:
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
+ HardwareBufferRenderParams mHardwareBufferParams;
std::function<std::function<void(bool)>(int32_t, int64_t)> mFrameCallback;
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
- nsecs_t mLastDequeueBufferDuration = 0;
- nsecs_t mLastTargetWorkDuration = 0;
- std::optional<HintSessionWrapper> mHintSessionWrapper;
-
bool mForceDrawFrame = false;
};
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index fc6b28d2e1ad..b8f8c9267ad8 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -18,6 +18,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <SkColorSpace.h>
#include <SkImageInfo.h>
#include <SkRect.h>
#include <cutils/compiler.h>
diff --git a/libs/hwui/renderthread/HardwareBufferRenderParams.h b/libs/hwui/renderthread/HardwareBufferRenderParams.h
new file mode 100644
index 000000000000..91fe3f6cf273
--- /dev/null
+++ b/libs/hwui/renderthread/HardwareBufferRenderParams.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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 HARDWAREBUFFERRENDERER_H_
+#define HARDWAREBUFFERRENDERER_H_
+
+#include <android-base/unique_fd.h>
+#include <android/hardware_buffer.h>
+
+#include "SkColorSpace.h"
+#include "SkMatrix.h"
+#include "SkSurface.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+using namespace android::uirenderer::renderthread;
+
+using RenderCallback = std::function<void(android::base::unique_fd&&, int)>;
+
+class RenderProxy;
+
+class HardwareBufferRenderParams {
+public:
+ HardwareBufferRenderParams() = default;
+ HardwareBufferRenderParams(const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
+ RenderCallback&& callback)
+ : mTransform(transform)
+ , mColorSpace(colorSpace)
+ , mRenderCallback(std::move(callback)) {}
+ const SkMatrix& getTransform() const { return mTransform; }
+ sk_sp<SkColorSpace> getColorSpace() const { return mColorSpace; }
+
+ void invokeRenderCallback(android::base::unique_fd&& fenceFd, int status) {
+ if (mRenderCallback) {
+ std::invoke(mRenderCallback, std::move(fenceFd), status);
+ }
+ }
+
+private:
+ SkMatrix mTransform = SkMatrix::I();
+ sk_sp<SkColorSpace> mColorSpace = SkColorSpace::MakeSRGB();
+ RenderCallback mRenderCallback = nullptr;
+};
+
+} // namespace renderthread
+} // namespace uirenderer
+} // namespace android
+#endif // HARDWAREBUFFERRENDERER_H_
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 000000000000..94c9d94a7c26
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <private/performance_hint_private.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.h"
+#include "thread/CommonPool.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+ handle_, "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+ handle_, "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+ LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+ "Failed to find required symbol APerformanceHint_sendHint!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+ if (mHintSession) {
+ gAPH_closeSessionFn(mHintSession);
+ }
+}
+
+bool HintSessionWrapper::useHintSession() {
+ if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+ if (mHintSession) return true;
+ // If session does not exist, create it;
+ // this defers session creation until we try to actually use it.
+ if (!mSessionValid) return false;
+ return init();
+}
+
+bool HintSessionWrapper::init() {
+ if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+ // Assume that if we return before the end, it broke
+ mSessionValid = false;
+
+ ensureAPerformanceHintBindingInitialized();
+
+ APerformanceHintManager* manager = gAPH_getManagerFn();
+ if (!manager) return false;
+
+ std::vector<pid_t> tids = CommonPool::getThreadIds();
+ tids.push_back(mUiThreadId);
+ tids.push_back(mRenderThreadId);
+
+ // Use a placeholder target value to initialize,
+ // this will always be replaced elsewhere before it gets used
+ int64_t defaultTargetDurationNanos = 16666667;
+ mHintSession =
+ gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+ mSessionValid = !!mHintSession;
+ return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+ if (!useHintSession()) return;
+ targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+ if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+ targetWorkDurationNanos > kSanityCheckLowerBound &&
+ targetWorkDurationNanos < kSanityCheckUpperBound) {
+ mLastTargetWorkDuration = targetWorkDurationNanos;
+ gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ }
+ mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+ if (!useHintSession()) return;
+ if (actualDurationNanos > kSanityCheckLowerBound &&
+ actualDurationNanos < kSanityCheckUpperBound) {
+ gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+ if (!useHintSession()) return;
+ nsecs_t now = systemTime();
+ if (now - mLastFrameNotification > kResetHintTimeout) {
+ gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ }
+ mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 000000000000..fcbc10185255
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+ HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+ ~HintSessionWrapper();
+
+ void updateTargetWorkDuration(long targetDurationNanos);
+ void reportActualWorkDuration(long actualDurationNanos);
+ void sendLoadResetHint();
+
+private:
+ bool useHintSession();
+ bool init();
+ APerformanceHintSession* mHintSession = nullptr;
+
+ nsecs_t mLastFrameNotification = 0;
+ nsecs_t mLastTargetWorkDuration = 0;
+
+ pid_t mUiThreadId;
+ pid_t mRenderThreadId;
+
+ bool mSessionValid = true;
+
+ static constexpr nsecs_t kResetHintTimeout = 100_ms;
+ static constexpr int64_t kSanityCheckLowerBound = 100_us;
+ static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index ef58bc553c23..715c17dfc895 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -16,16 +16,19 @@
#pragma once
+#include <SkColorSpace.h>
+#include <SkRect.h>
+#include <android-base/unique_fd.h>
+#include <utils/RefBase.h>
+
+#include "ColorMode.h"
#include "DamageAccumulator.h"
#include "FrameInfoVisualizer.h"
+#include "HardwareBufferRenderParams.h"
#include "LayerUpdateQueue.h"
#include "Lighting.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
-#include "ColorMode.h"
-
-#include <SkRect.h>
-#include <utils/RefBase.h>
class GrDirectContext;
@@ -63,10 +66,14 @@ public:
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
- FrameInfoVisualizer* profiler) = 0;
+ FrameInfoVisualizer* profiler,
+ const HardwareBufferRenderParams& bufferParams) = 0;
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
+ [[nodiscard]] virtual android::base::unique_fd flush() = 0;
+ virtual void setHardwareBuffer(AHardwareBuffer* hardwareBuffer) = 0;
+ virtual bool hasHardwareBuffer() = 0;
virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a44b498c81c1..ed01e322ffd9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -29,6 +29,10 @@
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkPicture.h>
+
#include <pthread.h>
namespace android {
@@ -38,11 +42,13 @@ namespace renderthread {
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
- mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
- return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+ pid_t uiThreadId = pthread_gettid_np(pthread_self());
+ pid_t renderThreadId = getRenderThreadTid();
+ mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+ return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+ uiThreadId, renderThreadId);
});
- mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
- pthread_gettid_np(pthread_self()), getRenderThreadTid());
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -51,7 +57,7 @@ RenderProxy::~RenderProxy() {
void RenderProxy::destroyContext() {
if (mContext) {
- mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -79,6 +85,18 @@ void RenderProxy::setName(const char* name) {
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
+void RenderProxy::setHardwareBuffer(AHardwareBuffer* buffer) {
+ if (buffer) {
+ AHardwareBuffer_acquire(buffer);
+ }
+ mRenderThread.queue().post([this, hardwareBuffer = buffer]() mutable {
+ mContext->setHardwareBuffer(hardwareBuffer);
+ if (hardwareBuffer) {
+ AHardwareBuffer_release(hardwareBuffer);
+ }
+ });
+}
+
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
if (window) { ANativeWindow_acquire(window); }
mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
@@ -192,7 +210,8 @@ void RenderProxy::trimMemory(int level) {
// Avoid creating a RenderThread to do a trimMemory.
if (RenderThread::hasInstance()) {
RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
+ const auto trimLevel = static_cast<TrimLevel>(level);
+ thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); });
}
}
@@ -201,7 +220,7 @@ void RenderProxy::purgeCaches() {
RenderThread& thread = RenderThread::getInstance();
thread.queue().post([&thread]() {
if (thread.getGrContext()) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ thread.cacheManager().trimMemory(TrimLevel::COMPLETE);
}
});
}
@@ -231,6 +250,10 @@ void RenderProxy::notifyFramePending() {
mRenderThread.queue().post([this]() { mContext->notifyFramePending(); });
}
+void RenderProxy::notifyCallbackPending() {
+ mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
+}
+
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
mRenderThread.queue().runSync([&]() {
std::lock_guard lock(mRenderThread.getJankDataMutex());
@@ -313,6 +336,10 @@ void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom)
mDrawFrameTask.setContentDrawBounds(left, top, right, bottom);
}
+void RenderProxy::setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
+ mDrawFrameTask.setHardwareBufferRenderParams(params);
+}
+
void RenderProxy::setPictureCapturedCallback(
const std::function<void(sk_sp<SkPicture>&&)>& callback) {
mRenderThread.queue().post(
@@ -360,12 +387,13 @@ void RenderProxy::setForceDark(bool enable) {
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
- SkBitmap* bitmap) {
+void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
auto& thread = RenderThread::getInstance();
- return static_cast<int>(thread.queue().runSync([&]() -> auto {
- return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
- }));
+ ANativeWindow_acquire(window);
+ thread.queue().post([&thread, window, request = std::move(request)] {
+ thread.readback().copySurfaceInto(window, request);
+ ANativeWindow_release(window);
+ });
}
void RenderProxy::prepareToDraw(Bitmap& bitmap) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ee9efd46e307..17cf6650f87d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -17,19 +17,25 @@
#ifndef RENDERPROXY_H_
#define RENDERPROXY_H_
-#include <SkBitmap.h>
+#include <SkRefCnt.h>
+#include <android/hardware_buffer.h>
#include <android/native_window.h>
-#include <cutils/compiler.h>
#include <android/surface_control.h>
+#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "../FrameMetricsObserver.h"
#include "../IContextFactory.h"
#include "ColorMode.h"
+#include "CopyRequest.h"
#include "DrawFrameTask.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
+class SkBitmap;
+class SkPicture;
+class SkImage;
+
namespace android {
class GraphicBuffer;
class Surface;
@@ -71,7 +77,7 @@ public:
void setSwapBehavior(SwapBehavior swapBehavior);
bool loadSystemProperties();
void setName(const char* name);
-
+ void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
void setSurfaceControl(ASurfaceControl* surfaceControl);
void allocateBuffers();
@@ -79,6 +85,7 @@ public:
void setStopped(bool stopped);
void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setLightGeometry(const Vector3& lightCenter, float lightRadius);
+ void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params);
void setOpaque(bool opaque);
void setColorMode(ColorMode mode);
int64_t* frameInfo();
@@ -104,6 +111,7 @@ public:
static int maxTextureSize();
void stopDrawing();
void notifyFramePending();
+ void notifyCallbackPending();
void dumpProfileInfo(int fd, int dumpFlags);
// Not exported, only used for testing
@@ -133,8 +141,7 @@ public:
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
- int bottom, SkBitmap* bitmap);
+ static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
static void prepareToDraw(Bitmap& bitmap);
static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 01b956cb3dd5..7a7f1abdd268 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -251,7 +251,7 @@ void RenderThread::initThreadLocals() {
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = VulkanManager::getInstance();
- mCacheManager = new CacheManager();
+ mCacheManager = new CacheManager(*this);
}
void RenderThread::setupFrameInterval() {
@@ -266,7 +266,7 @@ void RenderThread::requireGlContext() {
}
mEglManager->initialize();
- sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+ sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface();
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
@@ -453,6 +453,8 @@ bool RenderThread::threadLoop() {
// next vsync (oops), so none of the callbacks are run.
requestVsync();
}
+
+ mCacheManager->onThreadIdle();
}
return false;
@@ -502,6 +504,11 @@ void RenderThread::preload() {
HardwareBitmapUploader::initialize();
}
+void RenderThread::trimMemory(TrimLevel level) {
+ ATRACE_CALL();
+ cacheManager().trimMemory(level);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index c1f6790b25b2..0a89e5e944f8 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,11 +17,11 @@
#ifndef RENDERTHREAD_H_
#define RENDERTHREAD_H_
-#include <surface_control_private.h>
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
#include <private/android/choreographer.h>
+#include <surface_control_private.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -31,6 +31,7 @@
#include <set>
#include "CacheManager.h"
+#include "MemoryPolicy.h"
#include "ProfileDataContainer.h"
#include "RenderTask.h"
#include "TimeLord.h"
@@ -172,6 +173,8 @@ public:
return mASurfaceControlFunctions;
}
+ void trimMemory(TrimLevel level);
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b8c2bdf112f8..c5196eeccea3 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -51,7 +51,8 @@ typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore
#include "VulkanSurface.h"
#include "private/hwui/DrawVkInfo.h"
-class GrVkExtensions;
+#include <SkColorSpace.h>
+#include <SkRefCnt.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index beb71b727f51..26486669e712 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -20,6 +20,7 @@
#include <system/window.h>
#include <vulkan/vulkan.h>
+#include <SkColorSpace.h>
#include <SkRefCnt.h>
#include <SkSize.h>
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index d3c41191eef1..dc36a2e01815 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -19,6 +19,8 @@
#include <SkCanvasVirtualEnforcer.h>
#include <SkNoDrawCanvas.h>
+enum class SkBlendMode;
+
namespace android {
namespace uirenderer {
namespace test {
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 898c64bd4159..fd596d998dfd 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -28,10 +28,11 @@ const ui::StaticDisplayInfo& getDisplayInfo() {
#if HWUI_NULL_GPU
info.density = 2.f;
#else
- const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
- LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
+ const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ LOG_ALWAYS_FATAL_IF(ids.empty(), "%s: No displays", __FUNCTION__);
- const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &info);
+ const status_t status =
+ SurfaceComposerClient::getStaticDisplayInfo(ids.front().value, &info);
LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__);
#endif
return info;
@@ -48,7 +49,10 @@ const ui::DisplayMode& getActiveDisplayMode() {
config.xDpi = config.yDpi = 320.f;
config.refreshRate = 60.f;
#else
- const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ LOG_ALWAYS_FATAL_IF(ids.empty(), "%s: No displays", __FUNCTION__);
+
+ const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config);
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index 43df4a0b1576..e70d44c9c60a 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -19,6 +19,8 @@
#include "TestContext.h"
#include "TestUtils.h"
+#include <SkBlendMode.h>
+
#include <utils/Color.h>
namespace android {
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 491af4336f97..a4890ede8faa 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -26,7 +26,13 @@
#include <renderthread/VulkanManager.h>
#include <utils/Unicode.h>
+#include "SkCanvas.h"
#include "SkColorData.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPixmap.h"
+#include "SkRect.h"
+#include "SkSurface.h"
#include "SkUnPreMultiply.h"
namespace android {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index fcaa745e9fc6..9d5c13e5cd75 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -28,10 +28,20 @@
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
+#include <SkBitmap.h>
+#include <SkColor.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
+
#include <gtest/gtest.h>
#include <memory>
#include <unordered_map>
+class SkCanvas;
+class SkMatrix;
+class SkPath;
+struct SkRect;
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 5af7d43d7f66..19e87f851827 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -19,6 +19,7 @@
#include "utils/Color.h"
#include <SkBitmap.h>
+#include <SkBlendMode.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 03aeb55f129b..a07cdf720b50 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -14,7 +14,17 @@
* limitations under the License.
*/
-#include <SkImagePriv.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkShader.h>
+#include <SkTileMode.h>
#include "hwui/Paint.h"
#include "TestSceneBase.h"
#include "tests/common/BitmapAllocationTestUtils.h"
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 2a016ac1b5bc..3a1ea8c29963 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ClippingAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 4271d2f04b88..484289a8ef1d 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -20,6 +20,8 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
+
#include <cstdio>
class GlyphStressAnimation;
diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
index cbdb756b8fa7..de0ef6d595f8 100644
--- a/libs/hwui/tests/common/scenes/HwBitmap565.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
@@ -18,6 +18,12 @@
#include "tests/common/BitmapAllocationTestUtils.h"
#include "utils/Color.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkRefCnt.h>
+
class HwBitmap565;
static TestScene::Registrar _HwBitmap565(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 564354f04674..dfdd0d8727b9 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+#include <SkColorSpace.h>
#include <SkGradientShader.h>
#include <SkImagePriv.h>
#include <ui/PixelFormat.h>
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index cac2fb3d8d5c..2955fb25ec2c 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerAnimation;
static TestScene::Registrar _HwLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
index 77a59dfe6ba5..8c9a6147f47d 100644
--- a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerSizeAnimation;
static TestScene::Registrar _HwLayerSize(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
index f5e6b317529a..250b986e7e73 100644
--- a/libs/hwui/tests/common/scenes/JankyScene.cpp
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <unistd.h>
class JankyScene;
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 5eaf1853233a..f669dbc9323e 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include "tests/common/TestListViewSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class ListOfFadedTextAnimation;
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index d031923a112b..4a5d9468cd88 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -17,7 +17,16 @@
#include "TestSceneBase.h"
#include "tests/common/TestListViewSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
#include <SkFont.h>
+#include <SkFontTypes.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkScalar.h>
#include <cstdio>
class ListViewAnimation;
@@ -48,7 +57,7 @@ class ListViewAnimation : public TestListViewSceneBase {
128 * 3;
paint.setColor(bgDark ? Color::White : Color::Grey_700);
- SkFont font;
+ SkFont font;
font.setSize(size / 2);
char charToShow = 'A' + (rand() % 26);
const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index edadf78db051..13a438199ae5 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -19,19 +19,57 @@
#include "utils/Color.h"
#include "hwui/Paint.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkFont.h>
+
class MagnifierAnimation;
+using Rect = android::uirenderer::Rect;
+
static TestScene::Registrar _Magnifier(TestScene::Info{
"magnifier", "A sample magnifier using Readback",
TestScene::simpleCreateScene<MagnifierAnimation>});
+class BlockingCopyRequest : public CopyRequest {
+ sk_sp<Bitmap> mDestination;
+ std::mutex mLock;
+ std::condition_variable mCondVar;
+ CopyResult mResult;
+
+public:
+ BlockingCopyRequest(::Rect rect, sk_sp<Bitmap> bitmap)
+ : CopyRequest(rect), mDestination(bitmap) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ SkBitmap bitmap;
+ mDestination->getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ std::unique_lock _lock{mLock};
+ mResult = result;
+ mCondVar.notify_all();
+ }
+
+ CopyResult waitForResult() {
+ std::unique_lock _lock{mLock};
+ mCondVar.wait(_lock);
+ return mResult;
+ }
+};
+
class MagnifierAnimation : public TestScene {
public:
sp<RenderNode> card;
sp<RenderNode> zoomImageView;
+ sk_sp<Bitmap> magnifier;
+ std::shared_ptr<BlockingCopyRequest> copyRequest;
void createContent(int width, int height, Canvas& canvas) override {
magnifier = TestUtils::createBitmap(200, 100);
+ setupCopyRequest();
SkBitmap temp;
magnifier->getSkBitmap(&temp);
temp.eraseColor(Color::White);
@@ -61,19 +99,20 @@ public:
canvas.enableZ(false);
}
+ void setupCopyRequest() {
+ constexpr int x = 90;
+ constexpr int y = 325;
+ copyRequest = std::make_shared<BlockingCopyRequest>(
+ ::Rect(x, y, x + magnifier->width(), y + magnifier->height()), magnifier);
+ }
+
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
card->mutateStagingProperties().setTranslationX(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
if (renderTarget) {
- SkBitmap temp;
- magnifier->getSkBitmap(&temp);
- constexpr int x = 90;
- constexpr int y = 325;
- RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
- y + magnifier->height(), &temp);
+ RenderProxy::copySurfaceInto(renderTarget.get(), copyRequest);
+ copyRequest->waitForResult();
}
}
-
- sk_sp<Bitmap> magnifier;
};
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index 402c1ece2146..1a2af8382ad7 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
class OvalAnimation;
static TestScene::Registrar _Oval(TestScene::Info{"oval", "Draws 1 oval.",
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index fb1b000a995e..25cf4d61bf9d 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class PartialDamageAnimation;
static TestScene::Registrar _PartialDamage(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
index 1e343c1dd283..969514c50d14 100644
--- a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include <vector>
+#include <SkBlendMode.h>
+
#include "TestSceneBase.h"
class PathClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
index 716d3979bdcb..3caaf8236d8a 100644
--- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
+++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
@@ -16,6 +16,12 @@
#include "TestSceneBase.h"
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+
class ReadbackFromHardware;
static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
index 1c2507867f6e..27948f8b4b43 100644
--- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
@@ -17,6 +17,11 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkColor.h>
+#include <SkRefCnt.h>
+
class RecentsAnimation;
static TestScene::Registrar _Recents(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index f37bcbc3ee1b..99e785887b16 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class RectGridAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index e9f353d887f2..2c27969487d3 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <vector>
class RoundRectClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 252f539ffca9..ee30c131efbd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -16,6 +16,7 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
#include <string>
#include "TestSceneBase.h"
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 31a8ae1d38cd..d5060c758f93 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class SaveLayerAnimation;
static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index c13e80e8c204..827ddab118d9 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGrid2Animation;
static TestScene::Registrar _ShadowGrid2(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 772b98e32220..a4fb10c5081e 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGridAnimation;
static TestScene::Registrar _ShadowGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
index 0019da5fd80b..58c03727bc29 100644
--- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowShaderAnimation;
static TestScene::Registrar _ShadowShader(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 70a1557dcf6a..c0c3dfd9a8c4 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
#include <cstdio>
class ShapeAnimation;
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index a0bc5aa245d5..40f2ed081626 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -16,7 +16,9 @@
#include "TestSceneBase.h"
-#include <SkColorMatrixFilter.h>
+#include <SkBlendMode.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
#include <SkGradientShader.h>
class SimpleColorMatrixAnimation;
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
index 57a260c8d234..a9e7a34b5b3f 100644
--- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -16,6 +16,7 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class SimpleGradientAnimation;
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index e677549b7894..bb95490c1d39 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -14,7 +14,16 @@
* limitations under the License.
*/
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
#include <SkFont.h>
+#include <SkFontTypes.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRefCnt.h>
+#include <SkRRect.h>
#include <cstdio>
#include "TestSceneBase.h"
#include "hwui/Paint.h"
@@ -130,7 +139,7 @@ private:
roundRectPaint.setColor(Color::White);
if (addHolePunch) {
// Punch a hole but then cover it up, we don't want to actually see it
- canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)));
+ canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)), 1.f);
}
canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
@@ -227,4 +236,4 @@ class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation {
StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
bool haveHolePunch() override { return true; }
bool forceLayer() override { return true; }
-}; \ No newline at end of file
+};
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index d30903679bce..78146b8cabf2 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
+
class TextAnimation;
static TestScene::Registrar _Text(TestScene::Info{"text", "Draws a bunch of text.",
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index c6219c485b85..aff8ca1e26c7 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -14,7 +14,12 @@
* limitations under the License.
*/
+#include "SkBitmap.h"
#include "SkBlendMode.h"
+#include "SkColorFilter.h"
+#include "SkFont.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
#include "TestSceneBase.h"
#include "tests/common/BitmapAllocationTestUtils.h"
#include "hwui/Paint.h"
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9cd10759a834..a55b72534924 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -22,6 +22,8 @@
#include "pipeline/skia/SkiaDisplayList.h"
#include "tests/common/TestUtils.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::skiapipeline;
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 6aed251481bf..72946c4abdf0 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -19,6 +19,8 @@
#include "hwui/Canvas.h"
#include "RenderNode.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
index 2ec78a429481..138b3efd10ed 100644
--- a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
+++ b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp
@@ -29,7 +29,7 @@ AHardwareBuffer* allocHardwareBuffer() {
.height = 16,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
- .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
+ .usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
};
constexpr int kSucceeded = 0;
int status = AHardwareBuffer_allocate(&desc, &buffer);
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index edd3e4e4f4d4..508e1986b4e4 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -32,7 +32,8 @@ static size_t getCacheUsage(GrDirectContext* grContext) {
return cacheUsage;
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
+// TOOD(258700630): fix this test and re-enable
+RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
int32_t width = DeviceInfo::get()->getWidth();
int32_t height = DeviceInfo::get()->getHeight();
GrDirectContext* grContext = renderThread.getGrContext();
@@ -58,7 +59,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
// attempt to trim all memory while we still hold strong refs
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
// free the surfaces
@@ -75,11 +76,11 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
// UI hidden and make sure only some got purged (unique should remain)
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+ renderThread.cacheManager().trimMemory(TrimLevel::UI_HIDDEN);
ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
// complete and make sure all get purged
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
}
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c3590e10..9e376e32f8ea 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,9 +36,9 @@ RENDERTHREAD_TEST(CanvasContext, create) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
- ASSERT_FALSE(canvasContext->hasSurface());
+ ASSERT_FALSE(canvasContext->hasOutputTarget());
canvasContext->destroy();
}
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 2cf3456694b0..1f6edf36af25 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -23,9 +23,18 @@
#include <tests/common/CallCountingCanvas.h>
-#include "SkPictureRecorder.h"
+#include "SkBlendMode.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
#include "SkColor.h"
+#include "SkImageInfo.h"
#include "SkLatticeIter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPictureRecorder.h"
+#include "SkRRect.h"
+#include "SkRect.h"
+#include "SkRegion.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include <SkNoDrawCanvas.h>
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
index 7f2e1589ae6c..ec9ab90fa46b 100644
--- a/libs/hwui/tests/unit/EglManagerTests.cpp
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -20,6 +20,8 @@
#include "renderthread/RenderEffectCapabilityQuery.h"
#include "tests/common/TestContext.h"
+#include <SkColorSpace.h>
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 2a74afc5bb7a..96a0c6114682 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -19,6 +19,8 @@
#include <SkCanvas.h>
#include <gtest/gtest.h>
+class SkRRect;
+
namespace {
class TestCanvasBase : public SkCanvas {
diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
index 098b4ccea8cf..92fd8294a486 100644
--- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
+++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
@@ -14,17 +14,17 @@
* limitations under the License.
*/
+#include <android-base/macros.h>
#include <gtest/gtest.h>
-
-#include "protos/graphicsstats.pb.h"
-#include "service/GraphicsStatsService.h"
-
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "protos/graphicsstats.pb.h"
+#include "service/GraphicsStatsService.h"
+
using namespace android;
using namespace android::uirenderer;
@@ -49,11 +49,7 @@ std::string findRootPath() {
// No code left untested
TEST(GraphicsStats, findRootPath) {
-#ifdef __LP64__
- std::string expected = "/data/nativetest64/hwui_unit_tests";
-#else
- std::string expected = "/data/nativetest/hwui_unit_tests";
-#endif
+ std::string expected = "/data/local/tmp/nativetest/hwui_unit_tests/" ABI_STRING;
EXPECT_EQ(expected, findRootPath());
}
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b80ea55..596bd37e4cf5 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
@@ -334,7 +335,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -398,7 +399,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -518,7 +519,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -618,7 +619,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -634,7 +635,7 @@ namespace {
static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646b0a76..80796f4a4111 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@ RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
});
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
canvasContext->setSurface(nullptr);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 974d85a453db..576e9466d322 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -25,6 +25,8 @@
#include <cstdint>
#include "FileBlobCache.h"
#include "pipeline/skia/ShaderCache.h"
+#include <SkData.h>
+#include <SkRefCnt.h>
using namespace android::uirenderer::skiapipeline;
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index dc1b2e668dd0..c1ddbd36bcfd 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -16,9 +16,14 @@
#include "tests/common/TestUtils.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkColor.h>
#include <SkColorMatrixFilter.h>
#include <SkColorSpace.h>
-#include <SkImagePriv.h>
+#include <SkImageInfo.h>
+#include <SkPaint.h>
+#include <SkPath.h>
#include <SkPathOps.h>
#include <SkShader.h>
#include <gtest/gtest.h>
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index dae3c9435712..87c52161d68e 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -17,9 +17,19 @@
#include "tests/common/TestUtils.h"
#include <hwui/Paint.h>
+#include <SkAlphaType.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
#include <SkCanvasStateUtils.h>
+#include <SkColor.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <SkImageInfo.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkRefCnt.h>
+#include <SkSurface.h>
#include <gtest/gtest.h>
using namespace android;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4bf05a..f825d7c5d9cc 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
// Set up a Surface so that we can position the VectorDrawable offscreen.
test::TestContext testContext;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7419f8fd89f1..4d0595e03da6 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 15ecf5831f3a..ced667eb76e5 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkCanvas.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index ab23448ab93f..499afa039d1f 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -21,8 +21,11 @@
#include <sys/stat.h>
#include <utils/Log.h>
+#include "SkData.h"
#include "SkFontMgr.h"
+#include "SkRefCnt.h"
#include "SkStream.h"
+#include "SkTypeface.h"
#include "hwui/MinikinSkia.h"
#include "hwui/Typeface.h"
@@ -61,7 +64,7 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
std::vector<minikin::FontVariation>());
std::vector<std::shared_ptr<minikin::Font>> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
- return std::make_shared<minikin::FontFamily>(std::move(fonts));
+ return minikin::FontFamily::create(std::move(fonts));
}
std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
@@ -70,7 +73,8 @@ std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const ch
TEST(TypefaceTest, resolveDefault_and_setDefaultTest) {
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoVariable), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kRobotoVariable), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ nullptr /* fallback */));
EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get()));
// Keep the original to restore it later.
@@ -348,24 +352,24 @@ TEST(TypefaceTest, createAbsolute) {
TEST(TypefaceTest, createFromFamilies_Single) {
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
- std::unique_ptr<Typeface> regular(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, false));
+ std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 400, false, nullptr /* fallback */));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(false).build();
- std::unique_ptr<Typeface> bold(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, false));
+ std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 700, false, nullptr /* fallback */));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(true).build();
- std::unique_ptr<Typeface> italic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, true));
+ std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 400, true, nullptr /* fallback */));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -373,8 +377,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// In Java,
// new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(true).build();
- std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, true));
+ std::unique_ptr<Typeface> boldItalic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 700, true, nullptr /* fallback */));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -382,8 +386,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// In Java,
// new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(1100).setItalic(false).build();
- std::unique_ptr<Typeface> over1000(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 1100, false));
+ std::unique_ptr<Typeface> over1000(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 1100, false, nullptr /* fallback */));
EXPECT_EQ(1000, over1000->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
@@ -391,30 +395,33 @@ TEST(TypefaceTest, createFromFamilies_Single) {
TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
// In Java, new Typeface.Builder("Family-Regular.ttf").build();
- std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Family-Bold.ttf").build();
- std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
- makeSingleFamlyVector(kBoldFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> bold(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kBoldFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Family-Italic.ttf").build();
- std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
- makeSingleFamlyVector(kItalicFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> italic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kItalicFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, new Typeface.Builder("Family-BoldItalic.ttf").build();
- std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kBoldItalicFont),
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> boldItalic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kBoldItalicFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ nullptr /* fallback */));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -424,8 +431,9 @@ TEST(TypefaceTest, createFromFamilies_Family) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
buildFamily(kRegularFont), buildFamily(kBoldFont), buildFamily(kItalicFont),
buildFamily(kBoldItalicFont)};
- std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
- std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> typeface(
+ Typeface::createFromFamilies(std::move(families), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, typeface->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
@@ -433,10 +441,24 @@ TEST(TypefaceTest, createFromFamilies_Family) {
TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
buildFamily(kBoldFont), buildFamily(kItalicFont), buildFamily(kBoldItalicFont)};
- std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
- std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> typeface(
+ Typeface::createFromFamilies(std::move(families), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(700, typeface->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
+TEST(TypefaceTest, createFromFamilies_Family_withFallback) {
+ std::vector<std::shared_ptr<minikin::FontFamily>> fallbackFamilies = {
+ buildFamily(kBoldFont), buildFamily(kItalicFont), buildFamily(kBoldItalicFont)};
+ std::unique_ptr<Typeface> fallback(
+ Typeface::createFromFamilies(std::move(fallbackFamilies), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, fallback.get()));
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
+}
+
} // namespace
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 6d4c57413f00..c1c21bd7dfbf 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -21,6 +21,12 @@
#include "utils/MathUtils.h"
#include "utils/VectorDrawableUtils.h"
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPath.h>
+#include <SkRefCnt.h>
+#include <SkShader.h>
+
#include <functional>
namespace android {
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 94bcb1110e05..f44f9d0fe2d4 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -19,6 +19,7 @@
#include <GLES2/gl2.h>
#include <utils/Blur.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkPaint.h>
#include <SkShader.h>
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 45da008c3e8e..a83516791f33 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -22,14 +22,9 @@
#include "MouseCursorController.h"
+#include <input/Input.h>
#include <log/log.h>
-#include <SkBitmap.h>
-#include <SkBlendMode.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
namespace {
// Time to spend fading out the pointer completely.
const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
@@ -204,8 +199,7 @@ static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, i
width = viewport.deviceWidth;
height = viewport.deviceHeight;
- if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
- viewport.orientation == DISPLAY_ORIENTATION_270) {
+ if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
std::swap(width, height);
}
}
@@ -249,38 +243,42 @@ void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
// Undo the previous rotation.
switch (oldViewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = oldViewport.deviceHeight - y;
y = temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = oldViewport.deviceWidth - x;
y = oldViewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = y;
y = oldViewport.deviceWidth - temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Perform the new rotation.
switch (viewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = y;
y = viewport.deviceHeight - temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = viewport.deviceWidth - x;
y = viewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = viewport.deviceWidth - y;
y = temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Apply offsets to convert from the pixel center to the pixel top-left corner position
@@ -292,7 +290,7 @@ void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
updatePointerLocked();
}
-void MouseCursorController::updatePointerIcon(int32_t iconId) {
+void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
std::scoped_lock lock(mLock);
if (mLocked.requestedPointerType != iconId) {
@@ -305,7 +303,7 @@ void MouseCursorController::updatePointerIcon(int32_t iconId) {
void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
std::scoped_lock lock(mLock);
- const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId();
+ const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
mLocked.additionalMouseResources[iconId] = icon;
mLocked.requestedPointerType = iconId;
mLocked.updatePointerIcon = true;
@@ -340,7 +338,7 @@ bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(
}
bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
- std::map<int32_t, PointerAnimation>::const_iterator iter =
+ std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
mLocked.animationResources.find(mLocked.requestedPointerType);
if (iter == mLocked.animationResources.end()) {
return false;
@@ -386,10 +384,10 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
- std::map<int32_t, SpriteIcon>::const_iterator iter =
+ std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
if (iter != mLocked.additionalMouseResources.end()) {
- std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
+ std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
mLocked.animationResources.find(mLocked.requestedPointerType);
if (anim_iter != mLocked.animationResources.end()) {
mLocked.animationFrameIndex = 0;
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index c0ab58bd2e7e..208d33d7c717 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -54,7 +54,7 @@ public:
void unfade(PointerControllerInterface::Transition transition);
void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources);
- void updatePointerIcon(int32_t iconId);
+ void updatePointerIcon(PointerIconStyle iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
void reloadPointerResources(bool getAdditionalMouseResources);
@@ -88,10 +88,10 @@ private:
bool resourcesLoaded;
- std::map<int32_t, SpriteIcon> additionalMouseResources;
- std::map<int32_t, PointerAnimation> animationResources;
+ std::map<PointerIconStyle, SpriteIcon> additionalMouseResources;
+ std::map<PointerIconStyle, PointerAnimation> animationResources;
- int32_t requestedPointerType;
+ PointerIconStyle requestedPointerType;
int32_t buttonState;
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 10ea6512c724..099efd3a1a2f 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -22,10 +22,18 @@
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
+
+#include <mutex>
#include "PointerControllerContext.h"
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+
namespace android {
namespace {
@@ -223,7 +231,7 @@ void PointerController::clearSpots() {
}
void PointerController::clearSpotsLocked() {
- for (auto& [displayID, spotController] : mLocked.spotControllers) {
+ for (auto& [displayId, spotController] : mLocked.spotControllers) {
spotController.clearSpots();
}
}
@@ -235,7 +243,7 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout
void PointerController::reloadPointerResources() {
std::scoped_lock lock(getLock());
- for (auto& [displayID, spotController] : mLocked.spotControllers) {
+ for (auto& [displayId, spotController] : mLocked.spotControllers) {
spotController.reloadSpotResources();
}
@@ -264,7 +272,7 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
}
}
-void PointerController::updatePointerIcon(int32_t iconId) {
+void PointerController::updatePointerIcon(PointerIconStyle iconId) {
std::scoped_lock lock(getLock());
mCursorController.updatePointerIcon(iconId);
}
@@ -286,13 +294,13 @@ void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>&
std::scoped_lock lock(getLock());
for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
- int32_t displayID = it->first;
- if (!displayIdSet.count(displayID)) {
+ int32_t displayId = it->first;
+ if (!displayIdSet.count(displayId)) {
/*
* Ensures that an in-progress animation won't dereference
* a null pointer to TouchSpotController.
*/
- mContext.removeAnimationCallback(displayID);
+ mContext.removeAnimationCallback(displayId);
it = mLocked.spotControllers.erase(it);
} else {
++it;
@@ -313,4 +321,20 @@ 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::scoped_lock lock(getLock());
+ dump += StringPrintf(INDENT2 "Presentation: %s\n",
+ ftl::enum_string(mLocked.presentation).c_str());
+ dump += StringPrintf(INDENT2 "Pointer Display ID: %" PRIu32 "\n", mLocked.pointerDisplayId);
+ dump += StringPrintf(INDENT2 "Viewports:\n");
+ for (const auto& info : mLocked.mDisplayInfos) {
+ info.dump(dump, INDENT3);
+ }
+ dump += INDENT2 "Spot Controllers:\n";
+ for (const auto& [_, spotController] : mLocked.spotControllers) {
+ spotController.dump(dump, INDENT3);
+ }
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index eab030f71e1a..48d5a5756a69 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -27,6 +27,7 @@
#include <map>
#include <memory>
+#include <string>
#include <vector>
#include "MouseCursorController.h"
@@ -65,7 +66,7 @@ public:
BitSet32 spotIdBits, int32_t displayId);
virtual void clearSpots();
- void updatePointerIcon(int32_t iconId);
+ void updatePointerIcon(PointerIconStyle iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
@@ -75,6 +76,8 @@ public:
void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
+ void dump(std::string& dump);
+
protected:
using WindowListenerConsumer =
std::function<void(const sp<android::gui::WindowInfosListener>&)>;
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index c2bc1e020279..1797428b343f 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -75,10 +75,11 @@ public:
virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
virtual void loadAdditionalMouseResources(
- std::map<int32_t, SpriteIcon>* outResources,
- std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
- virtual int32_t getDefaultPointerIconId() = 0;
- virtual int32_t getCustomPointerIconId() = 0;
+ std::map<PointerIconStyle, SpriteIcon>* outResources,
+ std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
+ int32_t displayId) = 0;
+ virtual PointerIconStyle getDefaultPointerIconId() = 0;
+ virtual PointerIconStyle getCustomPointerIconId() = 0;
virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) = 0;
};
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 2b809eab4ae4..130b204954b4 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -131,8 +131,9 @@ void SpriteController::doUpdateSprites() {
update.state.surfaceHeight = update.state.icon.height();
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
- update.state.surfaceControl = obtainSurface(
- update.state.surfaceWidth, update.state.surfaceHeight);
+ update.state.surfaceControl =
+ obtainSurface(update.state.surfaceWidth, update.state.surfaceHeight,
+ update.state.displayId);
if (update.state.surfaceControl != NULL) {
update.surfaceChanged = surfaceChanged = true;
}
@@ -168,8 +169,8 @@ void SpriteController::doUpdateSprites() {
}
}
- // If surface is a new one, we have to set right layer stack.
- if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+ // If surface has changed to a new display, we have to reparent it.
+ if (update.state.dirty & DIRTY_DISPLAY_ID) {
t.reparent(update.state.surfaceControl, mParentSurfaceProvider(update.state.displayId));
needApplyTransaction = true;
}
@@ -242,15 +243,14 @@ void SpriteController::doUpdateSprites() {
&& (becomingVisible
|| (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
Parcel p;
- p.writeInt32(update.state.icon.style);
+ p.writeInt32(static_cast<int32_t>(update.state.icon.style));
p.writeFloat(update.state.icon.hotSpotX);
p.writeFloat(update.state.icon.hotSpotY);
// Pass cursor metadata in the sprite surface so that when Android is running as a
// client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
// update mouse cursor in the host OS.
- t.setMetadata(
- update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+ t.setMetadata(update.state.surfaceControl, gui::METADATA_MOUSE_CURSOR, p);
}
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
@@ -331,21 +331,28 @@ void SpriteController::ensureSurfaceComposerClient() {
}
}
-sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
+sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height,
+ int32_t displayId) {
ensureSurfaceComposerClient();
- sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
- String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eHidden |
- ISurfaceComposerClient::eCursorWindow);
- if (surfaceControl == NULL || !surfaceControl->isValid()) {
+ const sp<SurfaceControl> parent = mParentSurfaceProvider(displayId);
+ if (parent == nullptr) {
+ ALOGE("Failed to get the parent surface for pointers on display %d", displayId);
+ }
+
+ const sp<SurfaceControl> surfaceControl =
+ mSurfaceComposerClient->createSurface(String8("Sprite"), width, height,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden |
+ ISurfaceComposerClient::eCursorWindow,
+ parent ? parent->getHandle() : nullptr);
+ if (surfaceControl == nullptr || !surfaceControl->isValid()) {
ALOGE("Error creating sprite surface.");
- return NULL;
+ return nullptr;
}
return surfaceControl;
}
-
// --- SpriteController::SpriteImpl ---
SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 2e9cb9685c46..1f113c045360 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -265,7 +265,7 @@ private:
void doDisposeSurfaces();
void ensureSurfaceComposerClient();
- sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
+ sp<SurfaceControl> obtainSurface(int32_t width, int32_t height, int32_t displayId);
};
} // namespace android
diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h
index a257d7e89ebc..5f085bbd2374 100644
--- a/libs/input/SpriteIcon.h
+++ b/libs/input/SpriteIcon.h
@@ -19,6 +19,7 @@
#include <android/graphics/bitmap.h>
#include <gui/Surface.h>
+#include <input/Input.h>
namespace android {
@@ -26,12 +27,13 @@ namespace android {
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
- inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) {}
- inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY)
+ inline SpriteIcon() : style(PointerIconStyle::TYPE_NULL), hotSpotX(0), hotSpotY(0) {}
+ inline SpriteIcon(const graphics::Bitmap& bitmap, PointerIconStyle style, float hotSpotX,
+ float hotSpotY)
: bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {}
graphics::Bitmap bitmap;
- int32_t style;
+ PointerIconStyle style;
float hotSpotX;
float hotSpotY;
@@ -41,7 +43,7 @@ struct SpriteIcon {
inline void reset() {
bitmap.reset();
- style = 0;
+ style = PointerIconStyle::TYPE_NULL;
hotSpotX = 0;
hotSpotY = 0;
}
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index f7c685ff8ba6..d9fe5996bcff 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -21,13 +21,14 @@
#include "TouchSpotController.h"
+#include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
#include <log/log.h>
-#include <SkBitmap.h>
-#include <SkBlendMode.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
+#include <mutex>
+
+#define INDENT " "
+#define INDENT2 " "
namespace {
// Time to spend fading out the spot completely.
@@ -59,6 +60,12 @@ void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, fl
}
}
+void TouchSpotController::Spot::dump(std::string& out, const char* prefix) const {
+ out += prefix;
+ base::StringAppendF(&out, "Spot{id=%" PRIx32 ", alpha=%f, scale=%f, pos=[%f, %f]}\n", id, alpha,
+ scale, x, y);
+}
+
// --- TouchSpotController ---
TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
@@ -261,4 +268,22 @@ void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
mContext.addAnimationCallback(mDisplayId, func);
}
+void TouchSpotController::dump(std::string& out, const char* prefix) const {
+ using base::StringAppendF;
+ out += prefix;
+ out += "SpotController:\n";
+ out += prefix;
+ StringAppendF(&out, INDENT "DisplayId: %" PRId32 "\n", mDisplayId);
+ std::scoped_lock lock(mLock);
+ out += prefix;
+ StringAppendF(&out, INDENT "Animating: %s\n", toString(mLocked.animating));
+ out += prefix;
+ out += INDENT "Spots:\n";
+ std::string spotPrefix = prefix;
+ spotPrefix += INDENT2;
+ for (const auto& spot : mLocked.displaySpots) {
+ spot->dump(out, spotPrefix.c_str());
+ }
+}
+
} // namespace android
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
index 703de3603f48..5bbc75d9570b 100644
--- a/libs/input/TouchSpotController.h
+++ b/libs/input/TouchSpotController.h
@@ -38,6 +38,8 @@ public:
void reloadSpotResources();
bool doAnimations(nsecs_t timestamp);
+ void dump(std::string& out, const char* prefix = "") const;
+
private:
struct Spot {
static const uint32_t INVALID_ID = 0xffffffff;
@@ -58,6 +60,7 @@ private:
mLastIcon(nullptr) {}
void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
+ void dump(std::string& out, const char* prefix = "") const;
private:
const SpriteIcon* mLastIcon;
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index f9752ed155df..a6a4115476df 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-#include "mocks/MockSprite.h"
-#include "mocks/MockSpriteController.h"
-
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include <input/PointerController.h>
#include <input/SpriteController.h>
#include <atomic>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
#include <thread>
+#include "input/Input.h"
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
namespace android {
enum TestCursorType {
@@ -39,7 +40,6 @@ enum TestCursorType {
using ::testing::AllOf;
using ::testing::Field;
-using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Test;
@@ -52,10 +52,12 @@ class MockPointerControllerPolicyInterface : public PointerControllerPolicyInter
public:
virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
- virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
- std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
- virtual int32_t getDefaultPointerIconId() override;
- virtual int32_t getCustomPointerIconId() override;
+ virtual void loadAdditionalMouseResources(
+ std::map<PointerIconStyle, SpriteIcon>* outResources,
+ std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
+ int32_t displayId) override;
+ virtual PointerIconStyle getDefaultPointerIconId() override;
+ virtual PointerIconStyle getCustomPointerIconId() override;
virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
bool allResourcesAreLoaded();
@@ -85,34 +87,33 @@ void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources
}
void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
- std::map<int32_t, SpriteIcon>* outResources,
- std::map<int32_t, PointerAnimation>* outAnimationResources,
- int32_t) {
+ std::map<PointerIconStyle, SpriteIcon>* outResources,
+ std::map<PointerIconStyle, PointerAnimation>* outAnimationResources, int32_t) {
SpriteIcon icon;
PointerAnimation anim;
// CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
loadPointerIconForType(&icon, cursorType);
- (*outResources)[cursorType] = icon;
+ (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
// CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
loadPointerIconForType(&icon, cursorType);
anim.animationFrames.push_back(icon);
anim.durationPerFrame = 10;
- (*outResources)[cursorType] = icon;
- (*outAnimationResources)[cursorType] = anim;
+ (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
+ (*outAnimationResources)[static_cast<PointerIconStyle>(cursorType)] = anim;
additionalMouseResourcesLoaded = true;
}
-int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
- return CURSOR_TYPE_DEFAULT;
+PointerIconStyle MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+ return static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT);
}
-int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
- return CURSOR_TYPE_CUSTOM;
+PointerIconStyle MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+ return static_cast<PointerIconStyle>(CURSOR_TYPE_CUSTOM);
}
bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
@@ -124,7 +125,7 @@ bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
}
void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
- icon->style = type;
+ icon->style = static_cast<PointerIconStyle>(type);
std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
icon->hotSpotX = hotSpot.first;
icon->hotSpotY = hotSpot.second;
@@ -205,11 +206,11 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
- EXPECT_CALL(*mPointerSprite, setIcon(
- AllOf(
- Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
- Field(&SpriteIcon::hotSpotX, hotspot.first),
- Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ EXPECT_CALL(*mPointerSprite,
+ setIcon(AllOf(Field(&SpriteIcon::style,
+ static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
mPointerController->reloadPointerResources();
}
@@ -222,12 +223,11 @@ TEST_F(PointerControllerTest, updatePointerIcon) {
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
- EXPECT_CALL(*mPointerSprite, setIcon(
- AllOf(
- Field(&SpriteIcon::style, type),
- Field(&SpriteIcon::hotSpotX, hotspot.first),
- Field(&SpriteIcon::hotSpotY, hotspot.second))));
- mPointerController->updatePointerIcon(type);
+ EXPECT_CALL(*mPointerSprite,
+ setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
}
TEST_F(PointerControllerTest, setCustomPointerIcon) {
@@ -239,17 +239,16 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) {
float hotSpotY = 20;
SpriteIcon icon;
- icon.style = style;
+ icon.style = static_cast<PointerIconStyle>(style);
icon.hotSpotX = hotSpotX;
icon.hotSpotY = hotSpotY;
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
- EXPECT_CALL(*mPointerSprite, setIcon(
- AllOf(
- Field(&SpriteIcon::style, style),
- Field(&SpriteIcon::hotSpotX, hotSpotX),
- Field(&SpriteIcon::hotSpotY, hotSpotY))));
+ EXPECT_CALL(*mPointerSprite,
+ setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
+ Field(&SpriteIcon::hotSpotX, hotSpotX),
+ Field(&SpriteIcon::hotSpotY, hotSpotY))));
mPointerController->setCustomPointerIcon(icon);
}